Data in Arduino UNO's EEPROM changes after 3-4 days randomly

I am using the following code to read power from A0 analog port of an Arduino UNO. The offset value changes from transmitter to transmitter hardware. So it is calibrated once and store in the eeprom. Pin 3 is used to request for change of offset value, but it is called rarely.

The code works, but I finding that the offset value changes after 3-4 days automatically to random value. I am reading only once when the uP starts up. Writing to the EEPROM is done only once for the entire lifetime of the eqpt. I can't fathom why the offset value in the EEPROM changes after 3-4 days?

#include <AltSoftSerial.h> // buffer size 80
#include <EEPROM.h>
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

#define Max 23

AltSoftSerial altSerial; // pin(8, 9) (Rx, TX)

char is[50]; 
char id[]="MAN002";

int data100W = 412;
int offset;

int graph[Max][2] = {
  { 275, 0 }, { 355,5 }, { 366,10 }, { 372,15 }, { 378,20 },
  { 383, 25 }, { 385,30 }, { 389,35 }, { 392,40 }, { 394,45 },
  { 396, 50 }, { 399,55 }, { 401,60 }, { 403,65 }, { 404,70 },
  { 406, 75 }, { 407,80 }, { 408,85 }, { 410,90 }, { 411,95 },
  { 412, 100 }, { 413, 105 }, { 414, 110 }

};

bool ForceSampleReady = false;
bool timerset = true;
char tx_status[3];
char power[4]; 
char refl[3];
long randNumber; 

bool printSerial(char* ch = NULL);
bool printSerialTx(char* * ch = NULL);
bool TimeReadyForTrigger();
void ReadFromTransmitter();
void SyncOrResetGsmAndTx();
void SendSMS();
int predict(float a1, float b1, float a2, float b2, float a3);

bool ForceSampleFlag = false;
bool AdjOffsetFlag = false;

void ForceIntrRoutine()
{
  if(!ForceSampleFlag){
    ForceSampleFlag = true;
    digitalWrite(13, HIGH);
  }    
}

void AdjOffsetIntrRoutine()
{ 
  if(!AdjOffsetFlag)
  {
    AdjOffsetFlag = true;
    digitalWrite(13, HIGH);  
  }    
}

void setup()
{  
    attachInterrupt(digitalPinToInterrupt(2), ForceIntrRoutine, CHANGE); // FORCE SAMPLE
    attachInterrupt(digitalPinToInterrupt(3), AdjOffsetIntrRoutine, CHANGE); // SET OFFSET
    
    pinMode(A0, INPUT);
    pinMode(2,INPUT_PULLUP); // ForceSample
    pinMode(3,INPUT_PULLUP); // Adjust 100W offset   
    pinMode(11,OUTPUT); // GSM Reset pin 
    digitalWrite(11,LOW); // Pull up reset pin of GSM
    
    pinMode(10, INPUT_PULLUP);  // reset offset to 0
    pinMode(12, INPUT_PULLUP);
    
    pinMode(A0, INPUT);
    analogRead(0);
    delay(2000);
    
    pinMode(13,OUTPUT); 
    digitalWrite(13,HIGH); 
    
    Serial.begin(9600); 
    while (!Serial) ; // wait for serial
    Serial.println("");
    Serial.println("Transmitter Remote Monitoring");
    Serial.println("");
    altSerial.begin(9600); //uP to GSM
    delay(5000); // wait for altSerial    
  
    randomSeed(analogRead(0));
    
    offset = eRead();
    Serial.println("\n Offset Value :");
    Serial.println(offset);
    
    SyncOrResetGsmAndTx();
    Serial.println("Remote Monitoring is listening...");
    digitalWrite(13,LOW);
}

int predict(float a1, float b1, float a2, float b2, float a3)
{
	float K = (a1 - a3) / (a3 - a2);
	float b3 = (K * b2 + b1) / (K + 1.0);
	return b3;
}

void eWrite(int val)
{
  Serial.println("\nCalling eWrite. ");
  byte bytes0 = val >> 8;
  byte bytes1 = val &  0xFF;  
  EEPROM.write(100, bytes0);
  delay(500);
  EEPROM.write(101, bytes1); 
  delay(500);  
}

int eRead()
{
  Serial.println("\nCalling eRead. "); 
  byte bytes0 = EEPROM.read(100);
  delay(500);
  byte bytes1 = EEPROM.read(101);
  delay(500);
  return  (bytes0  << 8) + bytes1 ;
}

void SyncOrResetGsmAndTx(){  
  
  while(true){       
     bool result = false;  
     unsigned long start_time = millis();
     while(result == false){
        altSerial.println("AT");
        delay(3000);  
        if(printSerial("OK")){
           result = true;
           Serial.println("Synchronization with GSM modem sucessful."); 
           altSerial.println("AT+CMGF=1");
           delay(3000);
           altSerial.println("AT+IPR=9600");
           delay(3000);
           printSerial();
           return;
        } 
        else Serial.println("Failed to synchronize with GSM modem.");
        if( millis() - start_time> 6000UL) {
          Serial.println("GSM init timeout.");
          break;
        }
     }//while result == false   

     if( result == false){ 
       // time to reboot gsm 
       Serial.println("Reset");
       digitalWrite(11,HIGH); 
       delay(300); // Pull down 200ms to LOW
       digitalWrite(11,LOW);
       delay(6000L); // wait 10 sec for initialization of GSM
       // it will retry
     }  
  } //while(true)  
}

void loop()
{
  delay(100);  // reduced from 1 sec
  
  if(TimeReadyForTrigger()){
    SyncOrResetGsmAndTx();
    int result = -1;    
    ReadFromTransmitter();  
    SendSMS(); 
    delay(62000UL); // 1 min plus delay to avoid retriggering
  }   
  
  if(ForceSampleFlag){
     ForceSampleReady = true; 
     int result = -1;
     SyncOrResetGsmAndTx();
     ReadFromTransmitter();  
     SendSMS(); 
     ForceSampleFlag = false;
     digitalWrite(13,LOW);
  }   
    
  if(AdjOffsetFlag)
  {      
     int v = analogRead(0);
     //v = map(v,100,470,0,1023);
     eWrite(data100W - v );
     delay(3000);     
     digitalWrite(13, LOW);    
     AdjOffsetFlag = false;  
  }   
    
  if( digitalRead(10) == LOW) 
  {
    eWrite(0);
  }  
}

void ReadFromTransmitter()
{
  long total =0;
  int cal_watt;
  //int offset = eRead();  // new : offset is read during setup only 
  
  for(int j = 0 ; j < 25; j++){    
	int val = analogRead(0);        
        val = val + offset;
        //Serial.print(val); Serial.print("   ");
	if( val<= 350) cal_watt = 0;
	else if (val >=440) cal_watt = 110;
	else
	for (int i = 0; i < Max-1; i++)
	{
		if (val == graph[i][0])
		{
			cal_watt = graph[i][1] ;
		}
		else if (val == graph[i+1][0])
		{
			cal_watt = graph[i+1][1] ;
		}
		else if (val >= graph[i][0] && val <= graph[i + 1][0]) {
			cal_watt = predict(graph[i][0], graph[i][1], graph[i+1][0], graph[i+1][1], val) ;
		}
	}
         total += cal_watt;
       }
     
        cal_watt = int(total/25.0);
        Serial.print("offset ");Serial.print(offset);Serial.print("  ");
	Serial.print(cal_watt); Serial.println(" watts");        
        sprintf(power,"%03d",cal_watt);
	return ;
}

bool printSerial(char* response)
{
  char buffer[120];
  int i = 0;
  while(altSerial.available()>0 && i<118){
    char c = altSerial.read();
    buffer[i++] = c;
    Serial.print(c); //DEBUG
  }
   buffer[i++] = '\0';
   
   if(response != NULL){
      char* p = NULL;
      p = strstr(buffer, response);        
      if( p!= NULL) return true;
   }
  return false;
}


void SendSMS(){
  tmElements_t tm;
  
  //digitalWrite(13,LOW);
  //delay(1000);
  //digitalWrite(13,HIGH);
  
  RTC.read(tm);
  delay(3000);
  char end[2];
  end[0]=0x1a;
  end[1]='\0';
  char sms[120];  
  
  strcpy(tx_status,"ON");   
  strcpy(refl,"0");   
  
  //sprintf(sms,"ID=%s$Tx=%s$Power=%s$Refl=%s$Date=%02d/%02d/%04d$Time=%02d:%02d:%02d$", id,  (!FlagTxResponding?"NA" : tx_status?"ON":"OFF"), power, refl, tm.Day, tm.Month, tmYearToCalendar(tm.Year), tm.Hour, tm.Minute, tm.Second);  
  //"StnID=E732323&Date=07/06/2020&Time=14:49:26&Power=70&Refl=1&Status=ON"
  sprintf(sms,"StnID=%s&Date=%02d/%02d/%04d&Time=%02d:%02d:%02d&Power=%s&Refl=%s&Status=%s", 
    id,  
    tm.Day, tm.Month, tmYearToCalendar(tm.Year),
    tm.Hour, tm.Minute, tm.Second,    
    power,
    refl,    
    tx_status
    );
    
    //Serial.println(sms);
  // this may take upto 15sec if GSM resets or it may be in indefinite loop

  for(int repeats = 0; repeats < 1; repeats++){
     altSerial.println("AT+CMGS=\"+446033800000\"");  
     delay(3000);  
     if(printSerial(">")) {  Serial.println("Ready for SMS"); } else { Serial.println("Not ready for SMS"); continue;}
     
     //altSerial.print(sms);
     for(int i=0;i<strlen(sms);i++){
        altSerial.print(sms[i]); 
        delay(10);  
     }

   altSerial.write(0x1A); //println((char)26);
  
   bool time_out = false;
   unsigned long start_time = millis();   
   while (!printSerial("OK"))
   {
       //Serial.print("@");
       delay(1000UL); 
       if( millis() - start_time > 20000UL) {
         //Serial.println("GSM init timeout.");
          time_out = true;
          break;
       }
   }     
   if( !time_out) { Serial.println("SMS Sent");  break; } else { Serial.println("SMS not sent");  continue;}
     
     
   } //for
   }

 bool TimeReadyForTrigger(){
  tmElements_t tm;
  RTC.read(tm);
  delay(3000);
  
  int t[] = {6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21};
  
  //static bool first_time = true;
  //static int last_hr = -1;
  //Serial.println(tm.Minute);
  for(int i =0; i< 16; i++){     
     if(tm.Hour == t[i] && tm.Minute == 0 ){ //&& tm.Hour != last_hr){
       //Serial.println("Time trigger");
       //first_time = false;
       //last_hr = tm.Hour;       
       return true;
     } 
  }    
  return false;  
}



How do you know that it changed? I suggest that you print the value after reading it in setup().

Because you did not provide full code, it will be guess work. Possible reasons for unexpected behaviour

  1. eWrite() is called by accident.
  2. Writing outside the boundaries of an array.

On a side note, EEPROM.put() and EEPROM.get() can be used to write and read any data type (including integers) without jumping through hoops.

I remove the Serial.println commands to trim the code size before posting here. I have edited the code in the question slightlys, everthing else is as it is. I print the offset value once at start up and hourly also. Pin 2 of the arduino is used to request for change of offset value, but is called rarely.

@sterretje, your reply at sl# 2 needs more explaination. Thanks

And does it show that it changed at startup?

Your graph array must be filled somewhere with values, else it's a useles addition to your code ( :wink: ). It also must have a size that we can't see. You're using a variable Max that we can't see. What else is there that we don't know about?
So I suggest that you post your full code so we don't have to guess.

Assume that you have an array with 5 elements

int anArray[5];

and you somewhere do the below, you will have a problem.

anArray[5] = someValue;

If you can actually confirm with a minimal example sketch, that it is faulty EEPROM, it would be time to start looking at the power supply conditions at the time when it was written. Could it be that your software is writing values to EEPROM due to a software error? That is why a minimal sketch gets right to the point.

I don't see any loop() or setup().
Did you know that the EEPROM library, can write an 'int' to EEPROM in one call?

Just edited my code in the question, please see.

What is connected to pin 2, and how do you know that it is "called rarely"?

Put Serial.print() into the code (NOT in the interrupt routine) to inform when the pin 2 interrupt is activated.

Delays like this seem pointless:

       eWrite(data100W - v );
       delay(3000);      

Your interrupt handling is broken. There are a list of rules, you broke at least two. You didn't declare the variable volatile. You have a non-atomic read of the variable.

Another thing - if what is read is what was written, which can't be ruled out yet, then where is the post showing the code that writes data to EEPROM? That is important, no?

Then the question would also be - does the write sketch or code ever get run in the 3-4 day time frame that you mention? And what is your means of verification of the value that is read?

Also

Unless you use an external pullup or pulldown resistor on pin 3, your pin 3 is initially a floating input that might have set the interrupt flag. At the moment that you use attachInterrupt after that, it will trigger the interrupt.

Your code shows pin 3 ?

And lastly, it's still not full code :wink:

Sorry. It should read pin 3. I miss this in the question pinMode(3,INPUT_PULLUP); But it is present in the actual code. Thanks for the input.

Thanks for your interest. I haven't declared the variable as volatile, as you have pointed out, any probable issue because of that?. Since this single threaded, non-atomic read essential? I have shown the write part in the edited question. I have a bad taste about using globally variable in Arduino code. Is there are possibility that the variable AdjOffsetFlag was randomly turn on? that would be bad though. I am not using this variable elsewhere in the program.

Thanks. It should read as Pin3 , my bad. Pin3 is configured as a hardware interrupt pin to be triggered by a user, only once for calibration purpose.

So what can happen (only after a reset of the board)

  1. Pin 3 is initially floating and a change is detected; the corresponding flag in the External Interrupt Flag register is set.
  2. You set the pin to internal pull up.
  3. You use attachInterrupt to enable the interrupt.
  4. Because the corresponding flag in the External Interrupt Flag register is set in (1), the ISR is triggered.

Actually oops here, too. A bool should be a byte which is inherently atomic on the AVR platform. But it should still be volatile. It tells the compiler that the variable is in shared memory, and not a processor register for optimization.

In the actual code, now edited, sorry for the inconveniences, attachInterrupt is called first in the setup. I have checked by using a Serial.println in the eWrite to check for erratic write, there was none.

I can't fully understand your last message, but I know it's important. I have faced issues with global variables in my earlier experience. What is the prefer datatype in this use case?

Why can't you take the time to explain exactly what is connected to it? If it is floating, you will get random interrupts.

I kept Pin3 floating all the time since it is internally pull up by a resistor to 5V. During calibration, pin 3 is grounded momentarily using a switch. To avoid debounce on the pin, the variable AdjOffsetFlag is allow set only if the variable is false.

You still don't seem to get what full code is; if you would have posted your full code initially, it would have saved you all the edits. and we don't have to keep on guessing.

Anyway, if false triggers don't happen, my theory is not correct.