Adding EEPROM

Hello,

Now that I have my program doing exactly what I want it to do I would like to save the state of the LEDS using EEPROM or some other way when the arduino is powered down and when it boots back up it will remember the state of how it was. My programs code is below. Thanks in advanced for the help it has been very useful up to this point.

#include <EEPROM.h>
#include <elapsedMillis.h>

boolean Left, Right;
elapsedMillis Ltimer, Rtimer;


#define HOLDTIME 100         
#define LeftSensor   5       
#define RightSensor  4       
#define LeftSensor2  3      
#define RightSensor2 2       
#define LeftTrafficLamp   12
#define RightTrafficLamp  11 
#define LeftTrafficLamp2  10 
#define RightTrafficLamp2  9 

void setup() {
pinMode(LeftSensor,           INPUT);
pinMode(RightSensor,          INPUT);
pinMode(LeftSensor2,          INPUT);
pinMode(RightSensor2,         INPUT);
pinMode(LeftTrafficLamp,     OUTPUT);
pinMode(RightTrafficLamp,    OUTPUT);
pinMode(LeftTrafficLamp2,    OUTPUT);
pinMode(RightTrafficLamp2,   OUTPUT);
digitalWrite(LeftTrafficLamp,  HIGH);
digitalWrite(LeftTrafficLamp2, HIGH);
Serial.begin (9600);
while (! Serial); 
Serial.println("STARTING...");
Left   = 0;
Right  = 0;
}

enum { Waiting, TriggerLeft, GoingLeft, TriggerRight, GoingRight };

int getDOT(boolean Left, boolean Right) {
static int state = Waiting;
    
switch (state) {
case Waiting:
if (Left && !Right) { state = TriggerRight; }
if (!Left && Right) { state = TriggerLeft; 
Serial.println("WAITING..."); }
break;
    
case TriggerRight:
if (!Left) { state = Waiting; } 
if (Right) { state = GoingRight; 
Serial.println("BLOCK 1 UNOCCUPIED"); } 
break;
                  
case TriggerLeft:
if (!Right) { state = Waiting; }
if (Left) { state = GoingLeft; 
Serial.println("BLOCK 1 OCCUPIED"); } // DEBUG
break;
    
case GoingRight:
case GoingLeft:
if (!Left && !Right) { state = Waiting; }
break;
    
default:
state = Waiting;
}
return state;
} 


void loop() {

boolean testLeft   = digitalRead(LeftSensor) && digitalRead(LeftSensor2);
boolean testRight  = digitalRead(RightSensor) && digitalRead(RightSensor2);

if (Serial.available()) {
char ch = Serial.read();

if (ch == 't') {
Serial.println("STARTING LED TEST");
delay(1000);
digitalWrite(LeftTrafficLamp,   HIGH);
digitalWrite(RightTrafficLamp,  HIGH);
digitalWrite(LeftTrafficLamp2,  HIGH);
digitalWrite(RightTrafficLamp2, HIGH);
Serial.println("GREEN 1: ON,  RED 1: ON");
Serial.println("GREEN 2: ON,  RED 2: ON");
delay(1000);
digitalWrite(LeftTrafficLamp,   LOW);
digitalWrite(RightTrafficLamp,  LOW);
digitalWrite(LeftTrafficLamp2,  LOW);
digitalWrite(RightTrafficLamp2, LOW);
Serial.println("GREEN 1: OFF, RED 1: OFF");
Serial.println("GREEN 2: OFF, RED 2: OFF");
delay(1000);
digitalWrite(LeftTrafficLamp,   HIGH);
digitalWrite(RightTrafficLamp,   LOW);
digitalWrite(LeftTrafficLamp2,  HIGH);
digitalWrite(RightTrafficLamp2,  LOW);
Serial.println("GREEN 1: ON,  RED 1: OFF");
Serial.println("GREEN 2: ON,  RED 2: OFF");
delay(1000);
digitalWrite(LeftTrafficLamp,   LOW);
digitalWrite(RightTrafficLamp,  LOW);
digitalWrite(LeftTrafficLamp2,  LOW);
digitalWrite(RightTrafficLamp2, LOW);
Serial.println("GREEN 1: OFF, RED 1: OFF");
Serial.println("GREEN 2: OFF, RED 2: OFF");
delay(1000);
digitalWrite(LeftTrafficLamp,    LOW);
digitalWrite(RightTrafficLamp,  HIGH);
digitalWrite(LeftTrafficLamp2,   LOW);
digitalWrite(RightTrafficLamp2, HIGH);
Serial.println("GREEN 1: OFF, RED 1: ON");
Serial.println("GREEN 2: OFF, RED 2: ON");
delay(1000);
digitalWrite(LeftTrafficLamp,   LOW);
digitalWrite(RightTrafficLamp,  LOW);
digitalWrite(LeftTrafficLamp2,  LOW);
digitalWrite(RightTrafficLamp2, LOW);
Serial.println("GREEN 1: OFF, RED 1: OFF");
Serial.println("GREEN 2: OFF, RED 2: OFF");
delay(1000);
digitalWrite(LeftTrafficLamp,   HIGH);
digitalWrite(RightTrafficLamp,   LOW);
digitalWrite(LeftTrafficLamp2,  HIGH);
digitalWrite(RightTrafficLamp2,  LOW);
Serial.println("GREEN 1: ON,  RED 1: OFF");
Serial.println("GREEN 2: ON,  RED 2: OFF");
delay(1000);
Serial.println("LED TEST COMPLETE");
} 
} 

if (testLeft) {
Ltimer = 0; 
Left = 1;
} else if (Ltimer > HOLDTIME) {
Left = 0;
}
    
if (testRight) {
Rtimer = 0;
Right = 1;
} else if (Rtimer > HOLDTIME) {
Right = 0;
}

int DOT  = getDOT(Left, Right);

if (DOT == GoingRight) {
digitalWrite(LeftTrafficLamp,   HIGH);
digitalWrite(RightTrafficLamp,   LOW);
digitalWrite(LeftTrafficLamp2,   LOW);
digitalWrite(RightTrafficLamp2, HIGH);
} else if (DOT == GoingLeft) {
digitalWrite(LeftTrafficLamp,    LOW);
digitalWrite(RightTrafficLamp,  HIGH);
digitalWrite(LeftTrafficLamp2,  HIGH);
digitalWrite(RightTrafficLamp2,  LOW);
}


}

Everytime you do a digitalWrite() for the traffic lights, you can use

EEPROM.put(address, (byte)digitalRead(xxxxTrafficLight));

The above reads back the status of a traffic light and stores it in the location in eeprom specified by address. The cast to byte forces the readback result to be a byte). You need 4 addresses, one for each light; they simply are numbers 0..3.

In setup(), you can read each address back and assign to the respective traffic light using

byte lightStatus;
EEPROM.get(address, lightStatus);
digitalWrite(xxxxTrafficLight, lightStatus);

Note that you can only write an eeprom cell about 100,000 times. Therefor the use of the put function instead of write. The put function checks if the value in eeprom is the same as the value that you want to save; if so, it will not write.

If a light changes every 5 seconds, you will have a worse case of 138 hours before the allocated eeprom cell will die and the result of reading it back might not be what it should be. You can implement some wear-leveling (do a search; never needed it myself) where you basically spread the writing over different cells.

You might be better of using an external I2C eeprom that you can easily replace or an external FRAM module that you can write billions of times.

Note: code samples not tested.

Thanks for being very descriptive as I've read through alot of the pros and cons of using eeprom versus external eeprom....This is being used on a model train layout so when the time to shut off the layout the eeprom would be used to save the state of the signals/block occupancy of the layout.....so I do not think it would run out any time soon of 100,000 saves etc. external would eliminate that possibility.

OK

I can't figure out from your code if there is something automated or if it only reacts on commands from the serial port. If the latter, you can consider to save the status on the computer (needs a dedicated application).

You can also implement a serial command to save to eeprom. You still have some pins available so you can also consider to add a button to save to eeprom before powering down. These two will however not 'protect' from power failures.

You can add a voltage monitor. It depends on how the Arduino is powered. If it's powered from e.g. 9V on the barrel, you can use a voltage divider to bring it down to 5V (or 3.3V depending on Arduino model) and use analogRead to monitor; if it goes e.g. below 4.5V, save the status to eeprom. You will probably need a buffer capacitor (depending on how long the power supply can keep the voltage above 4.5 volt) to keep the Arduino alive long enough to complete the saving to eeprom.

And lastly, you can/should build a check in setup() to make sure that there are no forbidden states (e.g. all lights green); if that happens, fall back to e.g. all red.

One part of the code works on serial command just to test the LEDS to make sure they light up or if one is burnt out etc. The signals change colors once the sensors are crossed....I need to save the last state of the LED...say the sensor is crossed...the lights go from Green to Red...if the arduino is powered off and the train does not move....when the arduino powers on it should remember if the LED was red or not....I do have the code to make ALL LEDS be green on start upin setup()...I will remove that when saving the last state of the LEDs added.

Thanks for the help I finally got it to work with the following code:

void blockState() {
int val = EEPROM.read(Address);
Serial.print("Starting: Current EEPROM Value Is: ");
Serial.println(val);
  
if(val == 0) {
Serial.println("EEPROM State Set: Writing...");
digitalWrite(LeftTrafficLamp,   HIGH);
digitalWrite(RightTrafficLamp,   LOW);
digitalWrite(LeftTrafficLamp2,   LOW);
digitalWrite(RightTrafficLamp2, HIGH);
} else if (val == 1){
Serial.println("EEPROM State Set: Writing...");
digitalWrite(LeftTrafficLamp,    LOW);
digitalWrite(RightTrafficLamp,  HIGH);
digitalWrite(LeftTrafficLamp2,  HIGH);
digitalWrite(RightTrafficLamp2,  LOW);
}
Serial.println("Done Setting Block States.");
} // END VOID

Now I do have a question. Does the write happen ONLY if there is a different value in EEPROM?

The code in post #6 doesn't write to the EEPROM so it's difficult to answer your question.

The EEPROM.update() function will only consume a write cycle if the value being written is different.

Note that the 100,000 writes is a guaranteed minimum. In practice, you will get millions. You would be very unlucky to only get 100,000.

No, write always happens. You can use EEPROM.put to save to eeprom and EEPROM.get to retrieve from eeprom (see reply #1).

Please note that eeprom.read returns a byte, not an integer. An integer exists of two bytes and reading the like you do will only return half of the integer. There is a 50% change that you read the correct byte (this depends on the endianness). The get and put functions prevent that type of issues.

PS

} // END VOID

It's not void :wink: It's a function.

It's clearer to state

} // END blockState

@MorganS - Below is the code that is used to put the info into eeprom.

if (DOT == GoingRight) {
digitalWrite(LeftTrafficLamp,   HIGH);
digitalWrite(RightTrafficLamp,   LOW);
digitalWrite(LeftTrafficLamp2,   LOW);
digitalWrite(RightTrafficLamp2, HIGH);
EEPROM.put(Address, 0);// EEPROM TEST
} else if (DOT == GoingLeft) {
digitalWrite(LeftTrafficLamp,    LOW);
digitalWrite(RightTrafficLamp,  HIGH);
digitalWrite(LeftTrafficLamp2,  HIGH);
digitalWrite(RightTrafficLamp2,  LOW);
EEPROM.put(Address, 1);// EEPROM TEST
}

@sterretje - So put in the above code, which works, only puts into eeprom a different value correct?

Difficult to image it works (according to my knowledge). EEPROM.put takes a variable, not a constant.

byte eepromTest;
if (DOT == GoingRight) {
  digitalWrite(LeftTrafficLamp,   HIGH);
  digitalWrite(RightTrafficLamp,   LOW);
  digitalWrite(LeftTrafficLamp2,   LOW);
  digitalWrite(RightTrafficLamp2, HIGH); 
  eepromTest = 0;
}
else if (DOT == GoingLeft) {
  digitalWrite(LeftTrafficLamp,    LOW);
  digitalWrite(RightTrafficLamp,  HIGH);
  digitalWrite(LeftTrafficLamp2,  HIGH);
  digitalWrite(RightTrafficLamp2,  LOW);
  eepromTest = 1;
}
EEPROM.put(Address, eepromTest);// EEPROM TEST

@sterretje - Below is the complete code the sending and feedback.

byte eepromTest;   // EEPROM TEST
#define Address 0  // EEPROM ADDRESS

void setup () {
blockState();
}

if (DOT == GoingRight) {
digitalWrite(LeftTrafficLamp,   HIGH);
digitalWrite(RightTrafficLamp,   LOW);
digitalWrite(LeftTrafficLamp2,   LOW);
digitalWrite(RightTrafficLamp2, HIGH);
eepromTest = 0; // EEPROM TEST
} else if (DOT == GoingLeft) {
digitalWrite(LeftTrafficLamp,    LOW);
digitalWrite(RightTrafficLamp,  HIGH);
digitalWrite(LeftTrafficLamp2,  HIGH);
digitalWrite(RightTrafficLamp2,  LOW);
eepromTest = 1; // EEPROM TEST
}
EEPROM.put(Address, eepromTest);// EEPROM TEST

void blockState() {
int val = EEPROM.read(Address);
Serial.print("Starting: Current EEPROM Value Is: ");
Serial.println(val);
  
if(val == 0) {
Serial.println("EEPROM State Set: Writing...");
digitalWrite(LeftTrafficLamp,   HIGH);
digitalWrite(RightTrafficLamp,   LOW);
digitalWrite(LeftTrafficLamp2,   LOW);
digitalWrite(RightTrafficLamp2, HIGH);
} else if (val == 1){
Serial.println("EEPROM State Set: Writing...");
digitalWrite(LeftTrafficLamp,    LOW);
digitalWrite(RightTrafficLamp,  HIGH);
digitalWrite(LeftTrafficLamp2,  HIGH);
digitalWrite(RightTrafficLamp2,  LOW);
}
Serial.println("Done Setting Block States.");

sterretje:
Difficult to image it works (according to my knowledge). EEPROM.put takes a variable, not a constant.

The parameter type is const reference. It can be called with a constant. The compiler generates a dummy variable.

oqibidipo:
The parameter type is const reference. It can be called with a constant. The compiler generates a dummy variable.

Thanks for that, will try to remember.