Arduino Nano won't save number larger than 9 on EEPROM

*WARNING : LONG TOPIC"

Hi everyone,
I am continuing my Arduino dishwasher project. It is 95% completed, I was able to resolve my issue with the electromagnetic noises and have reorganized the code a little better. Howerver I am having an issue
First of all, let me introduce how the code works: each cycle can be comprised of 27 steps AT MOST. Shorter cycles, such as the "Rinse Only" will have fewer steps (in this case 6) and the same goes for the quick wash cycles.

The steps are as follows:

0-> initial drain
1-> resin regeneration (if necessary)
2-> prewash water fill
3-> prewash with cold water
4-> prewash heating
5-> prewash with hot water
6-> prewash drain
7-> main wash fill
8-> main wash with cold water
9-> main wash heating to 45°C
10-> main wash at 45°C
and so on and so forth. I won't list all of them since the problem happens here.

I have written a special function that, every 20ms (thanks to a timer 2 library, MsTimer2), checks to see if the voltage of the Arduino drops below 4.25 volts. If it does, it will save a few variables to EEPROM in order to prepare for a blackout. These variables are, namely:

-> the cycle number (eg. 1 = Pots And Pans, 2 = Regular, 3 = ECO ...)
-> the cycle phase (eg. phase number 6 = prewash drain)
-> the program state (eg. is the cycle running, is it paused, has an error occurred?)
-> the flowmeter pulses (so that, if the machine gets stopped during fill, it will resume from where it was stopped)
->the elapsed stage time before power failure (eg. phase 5 has started 2 minutes ago, this means that when the power is resumed the software will have to remember that this stage must last 2 minutes less)
-> the state of the outputs before power failure (so that they can be switched on when power is resumed)

Here is where the problem happens: if I run the "Rinse Only" cycle, which goes through steps 0,1,2,5,6 then if I simulate a black out by unplugging the machine, everything works fine. The cycle is resumed EXACTLY from where it left off

BUT, if I select the "Quick Wash" cycle which goes through steps 0,1,7,9,10 and unplug the machine during or after step 9, the machine won't restart the cycle correctly. I noticed that this happens due to the fact that the variable "elapsedTimeBeforePowerFailure" is not correctly updated. By switching the machine off and reading my EEPROM, here's what I found:

19:56:49.938 -> progIndex: 3
19:56:49.938 -> currentCycleState: 9
19:56:49.978 -> currentMacroState: 2
19:56:49.978 -> flowMeterPulses: 0
19:56:49.978 -> elapsedTimeBeforePowerFailure: 22
19:56:50.018 -> 0
19:56:50.018 -> 0
19:56:50.018 -> 1
19:56:50.018 -> 1
19:56:50.018 -> 1
19:56:50.057 -> 0

So the cycle number was saved correctly, the cycle state as well, same goes for the macro state (2= cycle running, 1 = delay timer countdown, 0 = cycle in selection phase). Thing is, I switched the machine off way after 22 milliseconds from the stage start time. In fact, the machine counts the time correctly, since stage 9 starts with opening the dispenser for 1.5 seconds and then shutting it off. This happens correctly and the time is counted down right, but WHY doesn't it save the elapsed time correctly?

AND, if I wait until the temperature has reached 45°C and the cycle advances to stage 10, if I switch it off then it will resume from stage 9. This happens every time stages go over 7 or 8 (I have not been able to pinpoint this exactly), which makes me think of some variables overflowing (?). If I stop the machine while it is filling for the main wash, as an example, the stage is 7 and it resumes correctly

Here's the code that updates the array that containt the output states before power failure (also used when pausing the cycle via the Start/Pause button)

void updateOutputStates() {
  for (byte m = 0; m <= regenSolenoid - drainPump; m++) {
    if (digitalRead(m + drainPump) != statesInWhichOutputsShouldBe[m] && currentMacroState != CYCLEPAUSED && currentMacroState != BLOCKINGERRORMODE) {
      statesInWhichOutputsShouldBe[m] = digitalRead(m + drainPump);
    }
  }
}

Here's the code that saves data to EEPROM if voltage goes below 4.25V

void powerFailure() {
  long VCC = readVcc();
  if (VCC < 4250 && prevVCC > VCC && doneWriting == false && (currentMacroState == DELAYCOUNT || currentMacroState == CYCLERUNNING || currentMacroState == CYCLEPAUSED)) {

    elapsedTimeBeforePowerFailure = millis() - stageStartTime;
    updateOutputStates();
    EEPROM.put(0, true); //flag for cycle interrupted while running due to power failure
    EEPROM.put(1, progIndex); //save selected program
    EEPROM.put(1 + sizeof(progIndex), currentCycleState); // save current cycle state
    EEPROM.put(1 + sizeof(progIndex) + sizeof(currentCycleState), currentMacroState); // save current macro state (eg. cycle running or others)
    EEPROM.put(1 + sizeof(progIndex) + sizeof(currentCycleState) + sizeof(currentMacroState), flowMeterPulses);
    EEPROM.put(1 + sizeof(progIndex) + sizeof(currentCycleState) + sizeof(currentMacroState) + sizeof(flowMeterPulses), elapsedTimeBeforePowerFailure);
    EEPROM.put(1 + sizeof(progIndex) + sizeof(currentCycleState) + sizeof(currentMacroState) + sizeof(flowMeterPulses) + sizeof(elapsedTimeBeforePowerFailure), statesInWhichOutputsShouldBe);

    doneWriting = true;

    //used for debugging short cycle not saving data
    Serial.println(elapsedTimeBeforePowerFailure);
  }
  prevVCC = VCC;
}

Here's the code that resumes data when starting again:

if (EEPROM.read(0) == true) { // for next iteration
    EEPROM.put(0, false);
    EEPROM.get(1, progIndex);
    EEPROM.get(1 + sizeof(progIndex), currentCycleState);
    EEPROM.get(1 + sizeof(progIndex) + sizeof(currentCycleState), currentMacroState);
    EEPROM.get(1 + sizeof(progIndex) + sizeof(currentCycleState) + sizeof(currentMacroState), flowMeterPulses);
    EEPROM.get(1 + sizeof(progIndex) + sizeof(currentCycleState) + sizeof(currentMacroState) + sizeof(flowMeterPulses), elapsedTimeBeforePowerFailure);
    //for (byte j = 0; j <= regenSolenoid - drainPump; j++) {
    //statesInWhichOutputsShouldBe[j] = EEPROM.get(4 + sizeof(flowMeterPulses) + sizeof(elapsedTimeBeforePowerFailure) + j, statesInWhichOutputsShouldBe[j]);
    EEPROM.get(1 + sizeof(progIndex) + sizeof(currentCycleState) + sizeof(currentMacroState) + sizeof(flowMeterPulses) + sizeof(elapsedTimeBeforePowerFailure), statesInWhichOutputsShouldBe);
    //}

    initTimeArray(); // do it here as it might be that the data from eeprom must initialize them
    assignParameters();
    isItPossibleToStart = true;
    stageStartTime = millis() - elapsedTimeBeforePowerFailure;
    elapsedTime = elapsedTimeBeforePowerFailure;//used when the cycle resumes after a pause, this way the machine resumes from where it left off once the door is closed again

    //timeDispenserWasOpened = millis();
    //isDispenserOpened = true;

    countDownFunction();

    if (currentMacroState != CYCLEPAUSED) {
      for (byte i = drainPump; i <= regenSolenoid; i++) {
        digitalWrite(i, statesInWhichOutputsShouldBe[i - drainPump]);//rewrite pins to the state they originally were before pausing the cycle
      }
    }
  }

Finally, here's the complete code attached

I want to thank everybody who will be able to help me in the slightest, I apologize for the topic length and wish you a good evening/morning/afternoon. Thanks

Arduino_Dishwasher_7SEG_CANDY.zip (21.0 KB)

I am not going to bother reading your dissertation, but will point out that EPROM memory is BYTES and you can save any binary number up to 255 in a single byte.

1 Like

Absolutely. This makes this event even more difficult to understand

If you are attempting to store the number as text characters, any number above 9 will require 2 characters. As pointed out, EEPROM is written to in bytes, and a byte can store only one character.

I am saving them as numbers. I am using EEPROM.put to save long type variables

Then it is time to begin debugging by tracking your number using serial.Print() in your program.

Rather than using the eeprom memory management scheme you are using, I would recommend putting all the data into a structure and saving and retrieving the entire structure with eeprom.put() and eeprom.get().

2 Likes

I have used Serial.print() to print the elapsed time. It advances correctly, but it saves it wrong
I have thought about using a struct but have no idea how to implement that

Time to write a simple test program which declares the struct, fills it with data, stores and retrieves it. There is an example in the library.

But I'd like not to implement new features until I have solved these bugs. Why can't it save any value over 8?

You will help us and help yourself if you can write an MCVE, "minimal, complete, verifiable example".

You have attached a 21KB zip file. It's not a realistic expectation that many on this forum are going to work their way though that to help solve the problem.

Before, you said larger than 9. Hence my suggestion that the value you were attempting to store was a character/text.

Guys, I figured it out
I had modified my internal VCC reading routine by making it non blocking. The issue was that when a few pins were switched on the voltage read would drop excessively, even though my oscilloscope wouldn't measure any voltage drop. In fact, that voltage drop wasn't there (from 5V to 2.4V). During stage 9 three outputs would be switched on: detergent dispenser, heater, washing pump. I reverted back to the old VCC reading routine and the problem is now solved. Thanks again to those who tried to help me

I'm just tuning in and I wonder if you have programmed it such that as the voltage is seen too low, and you write out the restart data, whether you then make it so it does not do that again and again as the voltage continues to drop…

There might otherwise be writing of data that is/has become corrupt or erroneous as the voltage gets too very low.

TBH just now under the umbrella wondering how one would even do that - write just once well before the ship sinks when you sense the fact that it is going to. Sink.

Now I worry that the writing process itself might not go too well at a certain point on the way down.

Something for me to google. And/or hear about here.

a7

void powerFailure() {
  long VCC = readVcc();
  if (VCC < 4250 && prevVCC > VCC && doneWriting == false && (currentMacroState == DELAYCOUNT || currentMacroState == CYCLERUNNING || currentMacroState == CYCLEPAUSED)) {

    elapsedTimeBeforePowerFailure = millis() - stageStartTime;
    updateOutputStates();
    EEPROM.put(0, true); //flag for cycle interrupted while running due to power failure
    EEPROM.put(1, progIndex); //save selected program
    EEPROM.put(1 + sizeof(progIndex), currentCycleState); // save current cycle state
    EEPROM.put(1 + sizeof(progIndex) + sizeof(currentCycleState), currentMacroState); // save current macro state (eg. cycle running or others)
    EEPROM.put(1 + sizeof(progIndex) + sizeof(currentCycleState) + sizeof(currentMacroState), flowMeterPulses);
    EEPROM.put(1 + sizeof(progIndex) + sizeof(currentCycleState) + sizeof(currentMacroState) + sizeof(flowMeterPulses), elapsedTimeBeforePowerFailure);
    EEPROM.put(1 + sizeof(progIndex) + sizeof(currentCycleState) + sizeof(currentMacroState) + sizeof(flowMeterPulses) + sizeof(elapsedTimeBeforePowerFailure), statesInWhichOutputsShouldBe);

    doneWriting = true;

    //used for debugging short cycle not saving data
    Serial.println(elapsedTimeBeforePowerFailure);
  }
  prevVCC = VCC;
}

The "doneWriting" variable takes care of that

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.