Crashing at EEPROM.put()

I am working with a particle photon and have recently upgraded from 0.6.2-rc.2 to 1.1.0-rc.2 and the code seems to crash at the instant I try to update the EEPROM memory.
A simple code to reproduce the problem:


SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
STARTUP(System.enableFeature(FEATURE_RESET_INFO));
STARTUP(System.buttonMirror(D1, FALLING));

volatile long lastDebounceTime;
unsigned long debounceDelay = 40;
volatile unsigned int pulseDelay = 1000;
int DAC_addr = 0;
int pulseStart = 0;
int counterLastEdge = LOW;
void counterHandler(void);
uint8_t pulseWidth = 100;


void setup()
{
	    Serial.begin(115200);
		pinMode(D1, INPUT_PULLUP);
		    pinMode(DAC, INPUT_PULLDOWN);
	    attachInterrupt(DAC, counterHandler, CHANGE);

		Particle.connect();

}



void counterHandler(void) {
	// int counter = 999;
	int counterEdge;
	if((millis() - lastDebounceTime) > debounceDelay) {
		lastDebounceTime = millis();
		counterEdge = pinReadFast(DAC);
		if(counterEdge != counterLastEdge) {
			if((counterEdge == LOW) && ((millis() - pulseStart) > pulseWidth)){
				int counter = 0;
				EEPROM.get(DAC_addr, counter);
				Serial.println("Increasing");
				counter++;
				Serial.println("Increased");
				EEPROM.put(DAC_addr, counter);
				Serial.println("Updated");
			} else if(counterEdge == HIGH){
				pulseStart = millis();
			}
			counterLastEdge = counterEdge;
		}
	}
}

At the serial output I am only getting Increased and after that it is crashing and blinking RED for Usage fault!

Also, I have found that the EEPROM.put() only seems to cause crash in the ISR function and not in the loop.

ISRs should do a very minimal amount of processing and return as quickly as possible. You are writing to serial and doing EEPROM get() and put() in your ISR… both operations can be kind of lengthy and use resources tied up by your main operations. Try moving those operations out of the ISR into loop(). In the ISR set a flag and in loop do the necessary processing when the flag is set.

You’re also accessing and updating counterLastEdge in the ISR but didn’t declare it as volitile… Was that intentional?

1 Like

To put it a bit more direct - EEPROM features have no business inside an ISR.
Neither should you have Serial.print() statements in there in production code - it’s “acceptable” in test code but definetly poor practice in production code.

Also about your repeated STARTUP() calls.
Have a look at the docs
https://docs.particle.io/reference/device-os/firmware/photon/#startup-

This method worked with the 0.6.2 version of device OS, however, I will still try your considerations.

It is a test code. :slight_smile:
And I will also look at your remarks for repeated STARTUP commands.

However, there is one thing that I would like you to know. The same code seems to work without fail if I remove the counter++ line, or, if I do EEPROM.put(addr, 0)
If what you are saying is true then there are two cases that arise now:
It was working for the device OS version 0.6.2 and not after updating to 1.1.0 unless there was a timeout restriction added in the later versions for the ISRs.
Second, why is it working when I am writing 0 into the EEPROM and crashing only when there is a larger value? If the ISR is failing because of time consumed by the EEPROM.put() function then it should not work regardless of the value I try to write.

This seems to be working for now. Thanks for the help. :slight_smile:

Also, my setup button mirror seems to have stopped working suddenly. The D1 pin is externally pulled up where it is connected to a button with a ground connection. It was working when the device OS was at 0.6.2 but it seems to have stopped responding.

I have even checked that the voltage is dropping at the pin after pressing the external button but, the photon doesn’t seem to respond.

Any guesses?

Flash behaves entirely differently to RAM, so special considerations have to be taken.

EEPROM (emulated via flash) uses wear leveling algorithms that may be able to put your new values down with very little delay but in other cases alternative locations may need to be found and worst case a page erase may be required which can take quite long.
So you may have success in multiple cases (e.g. during testing) but from time to time the code may crash.

Nope, not true - that’s one of the major differences between RAM and flash.

It very well depends on what data you intend to write to a particular cell.
Flash can only clear individual bits so when your “new” data does not need to set any bits but only clears them (e.g. by writing 0) the impact is less then when having to change individual bits from 0 to 1 - this can’t be done in place, hence a new location has to be found by the wear leveling algorithm potentially involving page erases.

3 Likes

An EEPROM.put allocates dynamic memory in the course of operation. Dynamic memory allocation is now restricted from an ISR context due to the potential for memory corruption. It would often work in quick testing but cause all sorts of weird and hard to diagnose issues in the field operating at scale and for long durations.

5 Likes

This answers a lot of my questions. Thank you.