Teensy 4.0 and EEPROM.h problem

DD4WH

Well-known member
I run into problems with EEPROM.h use with the Teensy 4.0. I would like to use the EEPROM to save about 800bytes.

The code below runs for (about) the first 20 EEPROM adresses, but fails with more. Additionally, the problem is not consistent, but the program fails to even print the first message to Serial when EEPROM_limit is too high. And the limit of 20 also seems to be a bit inconsistent, sometimes 19 is possible, sometimes not, sometimes 21 is possible, sometimes not. Am I doing something wrong?

Maybe the EEPROM.write writes the code to the flash where also program code is stored? Do I need to use other EEPROM adresses with the T4?

Code:
// Arduino 1.8.9, Teensyduino 1.47
// Teensy 4.0
// EEPROM problem
// which adresses to use?
// does the EEPROM write overwrite parts of the FLASH where the program is situated?
// it does not even print the "Before writing"-message, if I set EEPROM_limit above 20 . . . ???
// it seems the exact limit is flexible, gets me confused

#include <EEPROM.h>

// this works up to 20, but fails from 21 and higher
#define EEPROM_LIMIT  50

void setup() 
{
  // put your setup code here, to run once:
    Serial.begin(115200);
    delay(1000);
    Serial.println("Before writing");
    for(unsigned i = 0; i < EEPROM_LIMIT; i++)
    {
      EEPROM.write(i, 42);
    }
    Serial.println("After writing");
    for(unsigned i = 0; i < EEPROM_LIMIT; i++)
    {
      int r = EEPROM.read(i);
      Serial.print(i);
      Serial.print(") ");
      Serial.println(r);
    }
}

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

}
 
Thanks! I thought a delay(1000) was enough . . . ? And why is the Serial output dependent on the number of EEPROM adresses?
 
@manitou: thanks for the hint! However, it did not help.

The real problem I had was that in the Arduino compiler setting I had "optimize: smallest code" (which I did not realize . . .). That produced code that would not run reliably.

When I changed to: "Optimize: Faster", everything worked well!

All the best,

Frank DD4WH

P.S.: this was also the problem causing T4 lock with EEPROM.h defined in the Teensy Convolution SDR. Now with the right optimization setting ("Faster") that also works well :).
 
Last edited:
With the while(!Serial) and optimize "smallest code" I did not see any problems with your sketch ... ? 1.8.10 and 1.48
 
thanks for testing! Tested it again to be sure (with while(!Serial) :), but same problem occurs here: if I use "Optimize: smalles code" it works with 20, but not with 50 (prints out "Before writing", but nothing follows).

Could be my "old" version of Teensyduino . . . 1.8.9 and 1.47 ? will install the newest Arduino/Teensyduino versions and test later!
 
Installed Ardunio 1.8.10 and Teensyduino 1.48

Same effect: if I compile with "Optimize: smallest code", the script does not run properly when I set the limit above 20: it only prints out "Before writing" and bricks the Teensy.

If I compile with "Optimize: Faster", everything runs fine.

Confused . . .

Code:
// Arduino 1.8.10, Teensyduino 1.48
// Teensy 4.0
// EEPROM problem
// when compiled with "Optimize: smallest code"
// it does only print the "Before writing"-message, if I set EEPROM_limit above 20 . . . ???
// it seems the exact limit is flexible, gets me confused

#include <EEPROM.h>

// this works up to 20, but fails from 21 and higher
#define EEPROM_LIMIT  10

void setup() 
{
  // put your setup code here, to run once:
    Serial.begin(115200);
    while(!Serial);

    Serial.println("Before writing");
    for(unsigned i = 0; i < EEPROM_LIMIT; i++)
    {
      EEPROM.write(i, 42);
    }
    Serial.println("After writing");
    for(unsigned i = 0; i < EEPROM_LIMIT; i++)
    {
      int r = EEPROM.read(i);
      Serial.print(i);
      Serial.print(") ");
      Serial.println(r);
    }
}

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

}
 
Still works for me. I tried it on a 2nd T4 and the older T4B2R.

Although the first time i built sketch on 2nd T4 and T4B2R nothing printed and i had to hit the program button to load a new sketch. But I couldn't get it to fail again. 50 EEPROM writes on all 3 with optimize "smallest code" is working ...
 
BUT wait ... I have a sketch that prints out T4 EEPROM meta data https://github.com/manitou48/teensy4/blob/master/eeprom_meta.ino

I now recall that if the EEPROM data to be written matches what's already there, then no EEPROM write is required or done .... so i need to test with new values on each run ....

OK, if i change the value to be written, it hangs with "smallest code" -- nothing printed !! :confused:

"Houston, i think we have a problem ..." (or is this a known problem buried somewhere in the huge T4 beta test thread?)
 
...
"Houston, i think we have a problem ..." (or is this a known problem buried somewhere in the huge T4 beta test thread?)

Seems new AFAIK … FWIW - I don't recall any known/open issues/concerns with Teensy 4 EEPROM behavior.
 
I experimented with "optimization changes" to hardware/teensy/avr/cores/teensy4/eeprom.c while compiling with Optimize Smallest Code.

adding #pragma GCC optimize ("O2") to the front of eeprom.c worked
but the simplest change was instead adding
static void flash_wait() __attribute__((optimize("O2")));
suggesting timing issues may be in flash_wait()

of course there may be some changes to the flash register manipulation or while's that would fix it, but that is above my pay grade.
 
I am having this issue too.
If I choose "smallest code" for my T4 boombox I am now developing, It locks up completely as soon as I try to write to EEPROM.
Had me a bit confused until I remembered reading this thread.
 
Any news? I have a similar issue with the Teensy 4.0 EEPROM reliability after about 6 hours. Bad values are read back. My code is complicated so I won't show it for now, but I will say that the exact same code on ATMega328P (UNO) has been working for > 2 weeks. As my EEPROM is needed only when I power off/on and only before the next sensor measurements (< 1 hr.), I decided to not use the EEPROM for now. I may try to use an external EEPROM, try another Teensy, or try aforementioned code when I get some more time. Just curious if anyone has found work-arounds for it.
 
Any news? I have a similar issue with the Teensy 4.0 EEPROM reliability after about 6 hours. Bad values are read back. My code is complicated so I won't show it for now, but I will say that the exact same code on ATMega328P (UNO) has been working for > 2 weeks. As my EEPROM is needed only when I power off/on and only before the next sensor measurements (< 1 hr.), I decided to not use the EEPROM for now. I may try to use an external EEPROM, try another Teensy, or try aforementioned code when I get some more time. Just curious if anyone has found work-arounds for it.

What versions of Arduino & TD are you using ?? There were some EEPROM-related fixes which applied to the T4.x found during the testing of TD1.54 beta. My T4.1-based Teensy MIDIPolySynth is currently utilizing EEPROM to store multiple presets with no corruption detected. I am using Arduino 1.8.19 with TD1.57-beta3.

Mark J Culross
KD5RXT
 
What versions of Arduino & TD are you using ?? There were some EEPROM-related fixes which applied to the T4.x found during the testing of TD1.54 beta. My T4.1-based Teensy MIDIPolySynth is currently utilizing EEPROM to store multiple presets with no corruption detected. I am using Arduino 1.8.19 with TD1.57-beta3.

Mark J Culross
KD5RXT

I'm in between the issue version & your version.
Arduino 1.8.13 Teensyduino 1.55
Another task 4 me: Update my software.
Thanx!
Bill Glass
 
Any trend or pattern to the EEPROM use in the 'complicated' code?
> occasional or persistent reads
> total size of the read area
> read access method: 1,2,4 bytes? Struct read with get?

Below is my code (library with its sketch example). I'm receiving remote sensor measurements every hour. I display them on a tft, send them to ThingSpeak, and store them in EEPROM in case I power off. There are 6 strings that are updated by the sensors. Some change, some don't. I am using a wear-leveling algorithm to go easy on the EEPROM. Every time I write the 6 strings, they are appended to the previous (1 hour earlier) in a circular mode. When I read the 6 strings, the library fetches the latest. Here's the sequence.

IF HAS NEW SENSOR DATA
FETCH LATEST EEPROM DATA
UPDATE DATA WITH NEW SENSOR VALUES
STORE UPDATED DATA TO EEPROM
DISPLAY DATA ON TFT (ALWAYS)
UPDATE THINGSPEAK WITH CURRENT DATA EVERY HOUR


Code:
///////////////////////////////////// myLIBRARY /////////////////////////////////////

#ifndef sti
#define sti

#if (ARDUINO >=100)
  #include "Arduino.h"
#else
  #include "WProgram.h"
#endif
#include <EEPROM.h>
#define EEPROMsiz 1024

class StringsTointEEPROM
{
	public:
		//constructor
		StringsTointEEPROM();
		// Methods
		void format_eeprom();
		int begin();
		void save_string_to_eeprom(char *stringIn);
    		void save_nstrings_to_eeprom(int nbstring,char *stringsIn[]);
		void print_strings_from_eeprom();
		int get_string_from_eeprom(char *stringOut); //return 0 if MT
		int get_nstrings_from_eeprom(int nbstring, char *stringsOut[]); //return 0 if MT
	private:
		int find_eeprom_address();
		uint16_t _adr_EEPROM;
};
#endif



#include <stringsToint_EEPROM.h>

StringsTointEEPROM::StringsTointEEPROM()
{
}

void  StringsTointEEPROM::format_eeprom()
{
  for (int i=0;i<EEPROMsiz;i++) EEPROM.write(i,0xff);
}

int StringsTointEEPROM::begin()
{
  return find_eeprom_address();
}

void StringsTointEEPROM::save_string_to_eeprom(char *stringIn)
{
  // sync (0xaa)
  // nchar(0..255)
  // char[0]
  // char[1]
  //  ..
  // char[nchar-1]
  // 0xff (end of latest string)
  int i;
  uint8_t len,val=0;
  while (val!=0xff)
  { 
    val=EEPROM.read(_adr_EEPROM);
    _adr_EEPROM=(_adr_EEPROM+1)%EEPROMsiz;
  }
  _adr_EEPROM=(_adr_EEPROM-1)%EEPROMsiz;
  EEPROM.write(_adr_EEPROM,0xaa); //write sync
  _adr_EEPROM=(_adr_EEPROM+1)%EEPROMsiz;
  len=strlen(stringIn); 
  EEPROM.write(_adr_EEPROM,len);//write nchar
  _adr_EEPROM=(_adr_EEPROM+1)%EEPROMsiz;
  for(i = 0; i < len; i++)
  { 
    EEPROM.write(_adr_EEPROM, stringIn[i]);//write string
    _adr_EEPROM=(_adr_EEPROM+1)%EEPROMsiz;
  }
  EEPROM.write(_adr_EEPROM,0xff); //write end of string
}

void StringsTointEEPROM::save_nstrings_to_eeprom(int nbstring,char *stringsIn[])
{
  // sync (0xaa)
  // nchar(0..255)
  // char[0]
  // char[1]
  //  ..
  // char[nchar-1]
  // 0xff (end of latest string)
  int i,iter=0;
  uint8_t len,val=0;
  while (val!=0xff)
  { 
    val=EEPROM.read(_adr_EEPROM);
    _adr_EEPROM=(_adr_EEPROM+1)%EEPROMsiz;
  }
  _adr_EEPROM=(_adr_EEPROM-1)%EEPROMsiz;
  while (nbstring>0)
  {
    EEPROM.write(_adr_EEPROM,0xaa); //write sync
    _adr_EEPROM=(_adr_EEPROM+1)%EEPROMsiz;
    len=strlen(stringsIn[iter]); 
    EEPROM.write(_adr_EEPROM,len);//write nchar
    _adr_EEPROM=(_adr_EEPROM+1)%EEPROMsiz;
    for(i = 0; i < len; i++)
    { 
      EEPROM.write(_adr_EEPROM, stringsIn[iter][i]);//write string
      _adr_EEPROM=(_adr_EEPROM+1)%EEPROMsiz;
    }
    nbstring--;
    iter++;
  }
  EEPROM.write(_adr_EEPROM,0xff); //write end of string
}
void  StringsTointEEPROM::print_strings_from_eeprom()
{
  int i,cntr=0;
  uint8_t len,val=0;
  uint16_t adr_EEPROM=_adr_EEPROM;
  while (val!=0xff) //find end of latest string
  { 
    val=EEPROM.read(adr_EEPROM);
    adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
  }
  while(1) //display all strings that were stored in EEPROM in FIFO order
  {
    val=EEPROM.read(adr_EEPROM);
    while (val!=0xaa) //find sync (could be long if EEPROM not full)
    { 
      adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
      val=EEPROM.read(adr_EEPROM);
      cntr++;
      if (cntr==EEPROMsiz)
      {
        Serial.println("EEPROM is  MT");
        break;
      }
    }//while (val!=0xaa)
    if (cntr==EEPROMsiz) break;
    adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
    len=EEPROM.read(adr_EEPROM);
    adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
    for(i = 0; i < len; i++)
    { 
      Serial.print((char)EEPROM.read(adr_EEPROM));
      adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
    }//for(i = 0; i < len; i++)
    Serial.println();
    val=EEPROM.read(adr_EEPROM);
    if (val==0xff) break; //break if EEPROM not full or 1 full circle
    else val=0;
  }//while(1)
}

int StringsTointEEPROM::get_string_from_eeprom(char *stringOut)
{
  //get latest string from eeprom
  int i,cntr=0;
  uint8_t len,val=0;
  uint16_t adr_EEPROM=_adr_EEPROM;
  while (val!=0xff) //find end of latest string
  { 
    val=EEPROM.read(adr_EEPROM);
    adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
  }
  adr_EEPROM=(adr_EEPROM-1)%EEPROMsiz;
  while (val!=0xaa) //find sync
  { 
    val=EEPROM.read(adr_EEPROM);
    adr_EEPROM=(adr_EEPROM-1)%EEPROMsiz; //back up
    cntr++;
    if (cntr==EEPROMsiz) break; //EEPROM is MT
  }//while (val!=0xaa)
  if (cntr==EEPROMsiz) return 0;
  adr_EEPROM=(adr_EEPROM+2)%EEPROMsiz; //get to len
  len=EEPROM.read(adr_EEPROM);
  adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
  for(i = 0; i < len; i++)
  { 
    stringOut[i]=(char)EEPROM.read(adr_EEPROM);
    adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
  }//for(i = 1; i < len; i++)
  stringOut[len]='\0';
  return 1;
}

int StringsTointEEPROM::get_nstrings_from_eeprom(int nbstring,char *stringsOut[])
{
  int i,cntr=0,stringno;
  uint8_t len,val=0;
  uint16_t adr_EEPROM=_adr_EEPROM;
  while (val!=0xff) //find end of latest string
  { 
    val=EEPROM.read(adr_EEPROM);
    adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
  }
  adr_EEPROM=(adr_EEPROM-1)%EEPROMsiz;
  for (stringno=0;stringno<nbstring;stringno++)
  {
    while (val!=0xaa) //find sync
    { 
      val=EEPROM.read(adr_EEPROM);
      adr_EEPROM=(adr_EEPROM-1)%EEPROMsiz; //back up
      cntr++;
      if (cntr==EEPROMsiz) break; //EEPROM is MT
    }//while (val!=0xaa)
    val=0;
    if (cntr==EEPROMsiz) return 0;
  }//for (stringno=0;stringno<laststring+1;stringno++)
  adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz; //get to len
  for (stringno=0;stringno<nbstring;stringno++)
  {
    adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz; //get to len
    len=EEPROM.read(adr_EEPROM);
    adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
    for(i = 0; i < len; i++)
    { 
      stringsOut[stringno][i]=(char)EEPROM.read(adr_EEPROM);
      adr_EEPROM=(adr_EEPROM+1)%EEPROMsiz;
    }//for(i = 0; i < len; i++)
    stringsOut[stringno][len]='\0';
  }//for (stringno=0;stringno<nbstring;stringno++)
  return 1;
}

int  StringsTointEEPROM::find_eeprom_address()
{
  uint8_t val=0;
  int cntr=0;
  _adr_EEPROM=0;
  while (val!=0xff)
  {
    val=EEPROM.read(_adr_EEPROM);
    _adr_EEPROM=(_adr_EEPROM+1)%EEPROMsiz;
    cntr++;
    if (cntr==EEPROMsiz) return 0;
  }
  _adr_EEPROM=(_adr_EEPROM-1)%EEPROMsiz;
  return 1; 
}

//////////////////////////////////// myEXAMPLEscetch ///////////////////////////////////

#include <StringsToint_EEPROM.h>
#define TABLESIZ 6
StringsTointEEPROM inteeprom; //internal Arduino EEPROM Library for storing and retreiving c-strings

char eaudgc[10]="25";
char eaucm[10]="15";
char eautime[10]="(05:30)";
char airdgc[10]="18";
char airhum[10]="33";
char airtime[10]="(06:30)";
char *tableadr[]={eaudgc,eaucm,eautime,airdgc,airhum,airtime};




// STRUCTURE OF A STORED STRING
//-----------------------------
// sync (0xaa)
// nchar(0..255)
// char[0]
// char[1]
//  ..
// char[nchar-1]
// 0xff (end of latest string)
//-----------------------------


void setup() {
  Serial.begin(115200);
  delay(1000);
  //inteeprom.format_eeprom(); //needs to be formatted once
  if (inteeprom.begin()==0) Serial.println("begin() ERROR, Format the EEPROM");
  inteeprom.save_nstrings_to_eeprom(TABLESIZ,tableadr);
  Serial.println("latest 6 strings read from EEPROM");
  if (inteeprom.get_nstrings_from_eeprom(TABLESIZ,tableadr)==0) Serial.println("EEPROM is MT"); //read string
  Serial.println(eaudgc);
  Serial.println(eaucm);
  Serial.println(eautime);
  Serial.println(airdgc);
  Serial.println(airhum);
  Serial.println(airtime);
}

void loop() {
  // put your main code here, to run repeatedly:
}
 
Below is my code (library with its sketch example). I'm receiving remote sensor measurements every hour. I display them on a tft, send them to ThingSpeak, and store them in EEPROM in case I power off. There are 6 strings that are updated by the sensors. Some change, some don't. I am using a wear-leveling algorithm to go easy on the EEPROM. Every time I write the 6 strings, they are appended to the previous (1 hour earlier) in a circular mode. When I read the 6 strings, the library fetches the latest. Here's the sequence.

IF HAS NEW SENSOR DATA
FETCH LATEST EEPROM DATA
UPDATE DATA WITH NEW SENSOR VALUES
STORE UPDATED DATA TO EEPROM
DISPLAY DATA ON TFT (ALWAYS)
UPDATE THINGSPEAK WITH CURRENT DATA EVERY HOUR
...

Good info.
Missed reading the final part of this it seems (as p#19 show) - understood it was read only after restart:
"As my EEPROM is needed only when I power off/on and only before the next sensor measurements (< 1 hr.)"

So, with hourly updates of sensor - the Teensy fails after some 6 updates with code above in about 6 hours.

Seems the strings could fit into a fixed/max length area?

The EEPROM has built in 'low level' wear leveling - Paul described it once IIRC (long ago).
> seems each region of EEPROM in bands cycles around a fixed set of blocks supporting that band/group of EEPROM bytes?
> @Paul will hopefully correct if that isn't right.

Sounds like 'your' high level scheme in use is to extend farther into EEPROM space leaving early versions unchanged? Then at some point the 'END ADDRESS' is reached and it would then flush/restart in some fashion?

Any chance the failure happens on the reaching of the 'END ADDRESS' of available EEPROM and resetting to start?
 
Good info.
Missed reading the final part of this it seems (as p#19 show) - understood it was read only after restart:
"As my EEPROM is needed only when I power off/on and only before the next sensor measurements (< 1 hr.)"

So, with hourly updates of sensor - the Teensy fails after some 6 updates with code above in about 6 hours.

Seems the strings could fit into a fixed/max length area?

The EEPROM has built in 'low level' wear leveling - Paul described it once IIRC (long ago).
> seems each region of EEPROM in bands cycles around a fixed set of blocks supporting that band/group of EEPROM bytes?
> @Paul will hopefully correct if that isn't right.

Sounds like 'your' high level scheme in use is to extend farther into EEPROM space leaving early versions unchanged? Then at some point the 'END ADDRESS' is reached and it would then flush/restart in some fashion?

Any chance the failure happens on the reaching of the 'END ADDRESS' of available EEPROM and resetting to start?

Yes, "fails" gives corrupt info. after around 6 updates.
High level scheme just writes next 6 strings at the end of the 6 previous ones. At END ADDRESS it just continues writing at 0. This avoids continually writing the 6 strings at the same addresses. There are (6 strings)x(approx. 5 characters per string)=30 characters per write. 34 hours will be needed to fill the EEPROM and wrap the write address back to 0. That means writing address 0 every 34 hours instead of every hour. The time before failure for this EEPROM is now multiplied by 34.

The failure does not happen at END ADDRESS as I format (empty) the EEPROM before my test (6 6-string writes vs.34 6-string writes to get to END ADDRESS). No failure with the same library on the ATMega328 NANO (I said UNO by mistake above).

Thanks for your interest!

Bill Glass
 
Yes, "fails" gives corrupt info. after around 6 updates.
High level scheme just writes next 6 strings at the end of the 6 previous ones. At END ADDRESS it just continues writing at 0. This avoids continually writing the 6 strings at the same addresses. There are (6 strings)x(approx. 5 characters per string)=30 characters per write. 34 hours will be needed to fill the EEPROM and wrap the write address back to 0. That means writing address 0 every 34 hours instead of every hour. The time before failure for this EEPROM is now multiplied by 34.

The failure does not happen at END ADDRESS as I format (empty) the EEPROM before my test (6 6-string writes vs.34 6-string writes to get to END ADDRESS). No failure with the same library on the ATMega328 NANO (I said UNO by mistake above).

Thanks for your interest!

Bill Glass

Cool, that is less size than a glance at the code suggested for supported string size.

It working on 8-bit AVR is nice - brings the question: 1 byte, 2 byte, 4 byte vars and 'default' compile diff to 32 but ARM:
> are all vars properly sized explicitly? By definition or cast as needed?

There will be more than 30 bytes per 6 strings[5] with markers and such? NULLS's and or the 0xff?

A byte indexing 256 counts, would hold 6 set of 42 bytes, the 7'th instance would wrap at 36 bytes per string set?

Off hand seems the problem would be going the other direction, but that seems worth a look where the above math offers that hint.
 
If it were me I would try KISS code. Don’t try to do your own leveling code, instead simply store your strings each starting at a specified location.

Why code already stores each value, in a lookup pair, key,value. It also splits up consecutive items into their own block, where last item with your index wins… if no more room in page, it goes to replacement page and compresses out all of the old key/value pairs. So the fewer actual indexes used, the better.

Assuming my memory is correct, not at computer…
 
Cool, that is less size than a glance at the code suggested for supported string size.

It working on 8-bit AVR is nice - brings the question: 1 byte, 2 byte, 4 byte vars and 'default' compile diff to 32 but ARM:

I was under the impression that EEPROM.write(addr,val) stores a byte of data at addr of the EEPROM irregardless of whether it be a Nano or a Teensy.
 
If it were me I would try KISS code. Don’t try to do your own leveling code, instead simply store your strings each starting at a specified location.

Why code already stores each value, in a lookup pair, key,value. It also splits up consecutive items into their own block, where last item with your index wins… if no more room in page, it goes to replacement page and compresses out all of the old key/value pairs. So the fewer actual indexes used, the better.

Assuming my memory is correct, not at computer…

because it's already been done & working fine for Nano. Isn't it compatible with Teensy?
 
Back
Top