Making wites to a 24LC512 EEPROM faster

I have built a recording altimeter for a science fair project. I was able to download code from the Bear Altimeters website for basic functionality from this site Bear Altimeters. I have that all up and running but found that it took quite a bit of time for each successive in-flight record to be written close to 77ms giving me only 13 samples per second. I looked at the existing code and saw that there was a write delay of 10ms after each byte write making my 4 byte record incurr a delay of 40ms. I have changed the value to a 5ms delay which is what others have implemented and what agrees with the data sheet. This has saved me 20ms for a total delay of 56ms per record giving me 18 samples per second. The current code I am using is in this first code sample below.

void writeFlightRecord(long eltime, long altitude)
{
    i2c_eeprom_write_long(currentMemaddress, eltime);
    currentMemaddress += 2;
    i2c_eeprom_write_long(currentMemaddress, altitude);
    currentMemaddress += 2;
}


void i2c_eeprom_write_long( int memAddress, long memVar)
{
  byte memVarMSB = B00000000;
  byte memVarLSB = B00000000;

  memVarLSB = memVar;
  memVarMSB = memVar >> 8;
  i2c_eeprom_write_byte(0x50, memAddress, memVarMSB);
  i2c_eeprom_write_byte(0x50, memAddress + 1, memVarLSB);
}

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

I read in the data sheet that when writing one byte at a time the chip automatically incrementes the next write address implying that multiple bytes can be written to the chip before issuing the Wire.endTransmission() command thus allowing only one 5ms delay as the 4 byte buffer is written. I am hoping to shave an additional 15ms off my write interval with this technique. This would give me 25 samples per second. I have posted my proposed code below. Has anyone done this? Am I making correct assumptions? In the byte write mode do I need to worry about page boundaries? Any input would be most helpful.

void writeFlightRecordNew(long eltime, long altitude)
{
  int writeDelay=5;
  Wire.beginTransmission(0x50);
  Wire.write((int)(currentMemaddress >> 8)); // MSB
  Wire.write((int)(currentMemaddress & 0xFF)); // LSB
  Wire.write((int)(eltime >> 8));
  Wire.write((int)(eltime & 0xFF));
  Wire.write((int)(altitude >> 8));
  Wire.write((int)(altitude & 0xFF));
  Wire.endTransmission();
  delay(writeDelay);
  currentMemaddress += 4;
}

I figure that I have some leaning down of the main flight routine that may yield some additional ms as well. Ultimately I want to be able to specify the granularity of the data to how many records per second. I also want to pass the current address as a parameter instead of using a global variable.

Thanks for any help or ideas you have.

The Wire.setClock() can set the I2C speed higher. Default is 100kHz. About 200kHz is often possible, and many sensors support 400khz. At 400kHz the pullup resistors should be low in value (about 2k2 to 4k7).

Wire.setClock(400000L); // set to 400kHz.

A single Wire.write() can write more than one byte. Perhaps you don't even have to do those shifts for bytes.

Instead of waiting, the datasheet tells to do polling with the acknowledge.
The page write is 5ms. I'm not sure that is the delay that is also needed in the sketch.

This code checks the pages and waits until the device is ready : Arduino/I2C_eeprom.cpp at master · RobTillaart/Arduino · GitHub

A good place to start is with Nick Gammon's tutorial on I2C.

He has a section about multi byte writes to an external eeprom.

If you don't wish to handle page boundary issues or use a library to do so (I have used Jack Christensen's extEEPROM) Nick Gammon refers to the fact that if you limit your writes to 4 bytes, and begin at 0 or a multiple of 4, you will always avoid a boundary overwrite/wrap with a 32 or 64 byte page size. This fits well with your FlightData being a two integer (4 byte) array.

This jogs my thinking. Since after I write a flight record I return to code that itself creates a delay I should be able to write the 4 bytes with no delay continue processing and the write will be complete before my return. This would be predicated on writing all 4 bytes before ending.

I don't know if I can increase the bus speed since there is also a bmp180 on the bus. I could try and see what happens.

Running this on a pro mini. 3.3v. Could run at 5v with jumper change, this would speed the program execution but not communication with eeprom right?

Well there are some things to try. Isn't that most of the fun.

Running at 3V or 5V is the same executation speed.
The crystal or resonator is 8MHz or 16MHz, that is the execution speed.

You are right the pro mini I have runs at 16Mhz at 3.3v. I ran into a problem when first compiling because I chose a pro mini 3.3v 8Mhz and I got really strange results from the bmp180. I thought that the sensor was bad. Finaly someone in the forum here suggested that I look at compile settings. I recompiled using pro mini 5v 16Mhz and all was well. I also tried the code on a Uno clone running at 16Mhz and the execution speed is exactly the same.

I tried the new code sending 4 bytes at one time that works fine. I tried removing the delay after ending the communication and no records were recorded. So I have gotten the interval between record writes to be 40-41ms down from 75 ms so I am getting 25 samples a second. I may be able to get a ms or two if I implement new code that doesn't use a static delay. At this point the gain is so small that it isn't worth chasing.

It looks like any additional significant gains will have to come in the form of optimizing the control structure code eliminating as many actions as possible without compromising the functionality. Since the base code was written by someone else I can take a critical look at the structure and eliminate any functions and actions that I don't need, however the granularity is plenty good enough for this current project so it may end here.

Thanks for the help and a couple of additoinal sets of eyes.

Perhaps you don't have to eliminate functions. Search for delay() and while-loops, and look for those in the libraries as well. Anything timing based going on ? For example: DS18B20, DHT11, DHT22, NeoPixels.

Try putting all the data into a structure and write it with the EEPromAnything library. Increment the address pointer by sizeof(structure) for each write cycle. Read it back later into the same structure sequentially with the appropriate address increment.

Arctic_Eddie:
Try putting all the data into a structure and write it with the EEPromAnything library. Increment the address pointer by sizeof(structure) for each write cycle. Read it back later into the same structure sequentially with the appropriate address increment.

I thought EEPROMAnything was for writing to the internal eeprom on the arduino, not an external EEPROM chip. When I look at the EEPROMAnything.h file I see no reference to the i2c address necessary to write to the 24LC512 EEPROM chip.

I haven't seen anything like the ability to write a structure in the libraries for the 24LC512 chip.

Koepel:
Perhaps you don't have to eliminate functions. Search for delay() and while-loops, and look for those in the libraries as well. Anything timing based going on ? For example: DS18B20, DHT11, DHT22, NeoPixels.

I looked through the libraries that are called in the flight recording phase. There is a read of the 12c based BMP180 barametric pressure sensor that has a delay(5) in the read method. This delay seems to be standard for i2c based communications.

There is also a call to a kalman filter to smooth out the readings and help eliminate reading that are way outside the last reading. This however has no delays simply math and assignment of variables.

I have pasted the procedure that does all the flight control below. There are two loops but they are not delays simply a way to keep the program looping through until the flight ends. There is one delay after the main chute is deployed of one second. Though I am not using deployment on the water rockets we are using, this only creates a gap in the data at the position which is at the end of the flight and doesn't effect the intervals recorded eariler.

void recordAltitude()
{
  boolean exit=false;
  boolean apogeeReadyToFire = false;
  boolean mainReadyToFire = false;
  unsigned long apogeeStartTime =0;
  unsigned long mainStartTime =0;
  unsigned long flightAltitude=0;
  unsigned long flightEndAltitude = 0;
  unsigned long recordTime= millis()+1500;
  unsigned long currentTime=0;
  unsigned long diffTime=0;
  unsigned long ledmillis=0;
  	
  while (exit == false) 
  {

    if (!blinkOff)
    {
      if (ledState)
      {
        digitalWrite(13,LOW);
        ledState = false;
      }
      else
      {
        digitalWrite(13,HIGH);
        ledState = true;
      }
    }  
    //read current altitude
    currAltitude = (KalmanCalc(ReadAltitude())- initialAltitude);
    if (( currAltitude > config.liftOffAlt) == true && liftOff == false)
    {
      liftOff = true;
      // save the time
      initialTime = millis();
    }  
    if(liftOff)
    {
      Serial.println(F("we have lift off"));
      unsigned long prevTime=0;
      
      if(config.unit == 0)
        flightEndAltitude = 3;    //program will stop recording when rocket decends to a height of 3 meters
      else
        flightEndAltitude = 10;   //porgram will stop recording when rocket decents to a height of 10 feet

      //Loop to record flight data while main hasn't fired and rockent hasn't decended to flight end altitude                
      while(currAltitude > flightEndAltitude && mainHasFired==false)   
      {
		    flightAltitude = KalmanCalc(ReadAltitude());
        currAltitude = flightAltitude - initialAltitude;
		
        //if(canRecord && (recordTime >= millis()))
        if(canRecord)
        {
            currentTime = millis()- initialTime;
            diffTime = currentTime - prevTime;
            prevTime = currentTime;
            recordTime= millis() + recordInterval;
			      writeFlightRecord(currentTime,currAltitude);
        }
        
        if (flightAltitude < lastAltitude && apogeeHasFired == false)
        {
          measures = measures - 1;
        
          if (measures == 0)
          {
            apogeeReadyToFire = true;
            apogeeStartTime = millis();
            apogeeAltitude = lastAltitude;
          }  

        }
        else 
        {
          lastAltitude = flightAltitude;
          measures = NBR_MEASURE_APOGEE;
        }   
      
        if(apogeeReadyToFire)
        {
          if((millis()-apogeeStartTime) > config.outPut1Delay)
          {
            Serial.println("Drogue Fired");
            //fire drogue
            digitalWrite(pinApogee, HIGH);
            apogeeReadyToFire = false;
            apogeeHasFired=true;
          }
        }

        if ((currAltitude  < mainDeployAltitude) && apogeeHasFired == true && mainHasFired==false)
        {
          // Deploy main chute  X meters or feet  before landing...
          digitalWrite(pinApogee, LOW);
          mainReadyToFire = true;
          mainStartTime = millis();
          mainAltitude= currAltitude;     
        }
        if(mainReadyToFire)
        {
          Serial.println("conf delay main" + config.outPut1Delay );
          Serial.println("conf delay" + config.outPut2Delay );
          if((millis()-mainStartTime) > config.outPut2Delay)
          {
            //fire drogue
            Serial.println("firing main");
            digitalWrite(pinMain, HIGH);
            mainReadyToFire = false;
            mainHasFired=true;
          }
        }
      }
      
      if (canRecord)
      {
        Serial.println(F("stop recording\n "));
        saveFlight(currentFlight, startMemaddress, (currentMemaddress -1) , initialPressure, initialTemperature, initialAltitude, apogeeAltitude);
        writeHeight(apogeeAltitude - initialAltitude);
        printFlightSummary(currentFlight, config.unit);
        blinkOff = true;
      }
      
      exit =true;
      prevFlight=true;

      if (mainHasFired)
      {
        
        delay(1000);
        digitalWrite(pinMain, LOW);
        liftOff =false;
      }
    }
  }

There is no standard delay for I2C, but the BMP180 is sometimes busy (to capture data or converting).

The use of millis() prevents any delay(), and that is okay.

The way millis() is used, is confusing for me. I'm not sure it will be okay in all situations. Normally the millis() is remembered, and the test is done with: currentMillis - previousMillis. That will prevent the rollover problem.
In the sketch I see the millis() is added and substracted with values.
I have my doubts if this will always work: if ( recordTime >= millis ( ) ) )

25 samples per second, with 4 bytes each ? That is only 100 bytes per second. Somehow that seems slow. Perhaps too many other things are going on.
How often is the data from the BMP180 requested ? The same sample rate as writing data to the EEPROM ?
Suppose also 25 times per second, each with a 5ms delay, that makes: 125ms delay every second. That is a lot! The Arduino could be doing so much useful things in that time.
To avoid that, the library has to be rewritten.

if(canRecord)
        {
            currentTime = millis()- initialTime;
            diffTime = currentTime - prevTime;
            prevTime = currentTime;
            recordTime= millis() + recordInterval;
            writeFlightRecord(currentTime,currAltitude);
        }

What is the elapsed time (aka currentTime) of your flights?

You can get a measurement of loop speeds by recording some microsecond time values at points of interest.

@Compman2
You are probably correct in saying that EEPromAnything is for the internal memory. I used both, internal and external, about 5 years ago but can't find the project where the 24LC256 was implemented. I did find some info on writing long strings here. The function does not have any delays in the do/while loop but there is one at the end. These examples may help you.

Good luck.

I haven't seen anything like the ability to write a structure in the libraries for the 24LC512 chip.

I believe that both the Tillart and Christensen libraries referred to earlier (Reply #1 and #2) have block writes which can write structures with page boundary management. The Christensen library certainly does.

Koepel:
There is no standard delay for I2C, but the BMP180 is sometimes busy (to capture data or converting).

How often is the data from the BMP180 requested ? The same sample rate as writing data to the EEPROM ?
Suppose also 25 times per second, each with a 5ms delay, that makes: 125ms delay every second. That is a lot! The Arduino could be doing so much useful things in that time.
To avoid that, the library has to be rewritten.

Yes the BMP180 is read every time before a write to EEPROM.

I think it is the main code that is inefficient two many decisions. The sensor read takes 5ms and the record write takes 5ms so the remainder of the looping code requires 30-35ms. Many if statements thinking of rewriting the code using while statements.