Go with custom board with an FRAM for storing the data.
SRAM read/write speeds (so pretty much instantaneous compared to EEPROM), and EEPROM nonvolatility.
Here are 5V parts with SPI interface that would work great.
Would 4Mb of Flash Ram work? Use an ESP8266 board like the Wemos D1 Mini. It comes with 4mB of Flash that you can read and write to like a micro SD card. (It also operates at 80mHz as opposed to the 16mHz of the Uno).
If your encoder is not absolute position type, how about a "crankshaft position sensor" (That's what the $500 part in my Jeep is called. I asked for the old part and it turns out to simply be a hall-effect sensor and a magnet imbedded in the flywheel on the motor). This way you don't have to remember anything. One revolution calibrates your encoder position.
yes that part was already exists on stock engine before modding, i removed it. currently quite achieved what i'm trying to do but as stated on title i can't trust to the arduino board in a real world application. one accidental relay hiccup possibly damage to the engine, even might lead to an explosion.
I don't mean to be negative and I may still misunderstand but are you concerned you system might get out of sync if the encoder count pickups a voltage spike and slowly gets out of sync. Automotive electrical systems are terribly noisy. I can't see running a motor for 100's of hours and keeping things in absolute sync by counting the encoder only.
I think you will need a more absolute reference, perhaps a 1/rev signal?
Typically, the encoder looks like a gear with one tooth missing. There is a sensor that pulses as each tooth goes by. The electronics count the teeth going by to measure RPM and looks for the gap in the sequence for its zero mark. The number of teeth is usually in the 30 to 60 range.
Here is a sketch I posted in the forum back in 2018.
It reads a crank position encoder with "12" teeth (actually 11).
volatile unsigned long PulseInterval; // Microseconds between the last two pulses
volatile unsigned long PulseTime; // Time (micros()) of the rising edge of the latest pulse
volatile byte PulseNumber = 0; // Which pulse was the latest pulse
volatile boolean PulseIsNew = false; // Flag set by the ISR
static unsigned long prevPulseTime; // Needed for detecting the new pulse interval
static unsigned long prevPulseInterval; // Needed for detecting the long pulse
PulseTime = micros();
prevPulseInterval = PulseInterval; // save the preceding value
PulseInterval = PulseTime - prevPulseTime;
prevPulseTime = PulseTime;
// A pulse longer than 1.5 times the previous pulse indicates the index pulse (#0).
if (PulseInterval > (prevPulseInterval + (prevPulseInterval >> 1))) // compare to prev * 1.5
PulseNumber = 0;
PulseIsNew = true; // Let loop() know that new data is available
attachInterrupt(digitalPinToInterrupt(2), myPulseIsr, RISING);
static unsigned long localPulseTime = 0;
static byte localPulseNumber = 0;
static unsigned long microsecondsPerRevolution = 999;
// Grab the volatile variables.
unsigned long localPulseInterval = PulseInterval; // Should work down to 152 RPM
localPulseTime = PulseTime;
byte localPulseNumber = PulseNumber;
PulseIsNew = false;
microsecondsPerRevolution = localPulseInterval * 12;
if (localPulseNumber == 0)
microsecondsPerRevolution = localPulseInterval * 6; // Long Pulse
unsigned long microsecondsSincePulse = micros() - localPulseTime;
long int degreesSinceIndex = 30 * localPulseNumber; // First 11 pulses are at 30° intervals
// Equivalent to (microsecondsPerRevolution / microsecondsSincePulse) * 360 but less subject to truncation
degreesSinceIndex += (microsecondsPerRevolution * 360UL) / microsecondsSincePulse;
// THIS IS WHERE YOU ADJUST 'degreesSinceIndex' FOR OFFSET FROM TDC
// This is where you use the new crank position