9

Is there any way to store the values of necessary variables somewhere (may be in a file that gets updated by the program at various events), which can be read back when program starts after rebooting the Arduino board?

I am trying to control my AC appliances using IR remote control. Where I want Arduino to remember what was on and what was off in my room after a power failure.

Using EEPROM looks like a fairly good solution, though it has a limitation of 100,000 writes which is making me try to find another way out.

Using an external EEPROM which doesn't have that limitation or is fairly big, is a really good idea I got from fuenfundachtzig, thanks for this great idea. But I don't know how to implement this in my project. Any resource to gather knowledge on using external EEPROM, will be much appreciated.

Rohit Gupta
  • 618
  • 2
  • 5
  • 18

7 Answers7

7

The internal RAM of the Arduino will be reset when you repower the chip, so if you want to keep your data, you need to store it in EEPROM.

If you are worried about the limited write/erase cycles, you should estimate how often the data would be updated (i.e. written to EEPROM) and how long you plan the lifetime of the device you build. If 100,000 cycles then still is an issue, you have several options:

  • Keep the variables in RAM and only write them to EEPROM say every 5 minutes (100,000 cycles then makes about one year of continuous usage until the EEPROM might fail).
  • Use an external EEPROM (in a socket) that could be easily replaced if needed.
  • If replacing it is not an option, consider using an FRAM instead (advantage vs EEPROM: much more erase/write cycles but also very expensive, so not an option for large amounts of data but certainly for a few variables).

Note, however, that your description sounds like a typical use-case for user settings that you don't want to get lost on reboot. For those the internal EEPROM is the right place: only store them when they're changed and you're golden -- the user won't change them a 100,000 times easily...

fuenfundachtzig
  • 1,535
  • 1
  • 14
  • 26
3

EEPROM space, as already mentioned, will work if non-volatile is required. However, you have not qualified if it needs to be non-volatile or not. If not then you can use an __attribute__ to define the variable space not to be initialized. As the SRAM on the ATmega's are not cleared by reset or power cycle. The Compiler defaults to initializing them. Where this can be turned off per variable as in the below example:

//non-initialized value
union configUnion{
  uint8_t    byte[6]; // match the below struct...
  struct {
    uint16_t value1;
    uint16_t value2;
    uint16_t chksum;
  } val ;
} config  __attribute__ ((section (".noinit")));

void setup() {
  uint16_t sum; 
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  // prints title with ending line break
  Serial.print("Out of Reset -");
  sum = getchksum();
  printValues();

  if (sum != config.val.chksum) {
    config.val.chksum = sum;
    Serial.print("chksum is incorrect setting config.val.chksum = 0x"); Serial.println(config.val.chksum, HEX);
  }

  config.val.value1++;
  config.val.value2++;
  Serial.print("setup new values - ");
  printValues();
  config.val.chksum = getchksum();
  Serial.print("updating chksum config.val.chksum = 0x"); Serial.println(config.val.chksum, HEX);
}

int counter = 0;

void loop() {
  if (counter < 200) {
    Serial.print("after a while - ");
    printValues();
    Serial.println();
    while (true) {
      continue;
    }
  }
  counter++;
}

void printValues() {
  Serial.print(" value1 = 0x"); Serial.print(config.val.value1, HEX);
  Serial.print(", value2 = 0x"); Serial.print(config.val.value2, HEX);
  Serial.print(", sum = 0x"); Serial.print(getchksum(), HEX);
  Serial.print(", chksum = 0x"); Serial.println(config.val.chksum, HEX);
}

uint16_t getchksum() {
  int sum = 0;
  for (int position = 0; position < (sizeof(config) - sizeof(config.val.chksum)); position++) {
    sum = sum + config.byte[position];
  }
  return sum;
}

Output of above code is below. Note that the first output was the result of powering the UNO up with the Reset button depressed and then released after the Monitor window was started. Otherwise the "incorrect setting" print would have been missed, before the monitor window can be started.

Out of Reset - value1 = 0xBFED, value2 = 0xD2F9, sum = 0x377, chksum = 0xF457
chksum is incorrect setting config.val.chksum = 0x377
setup new values -  value1 = 0xBFEE, value2 = 0xD2FA, sum = 0x379, chksum = 0x377
updating chksum config.val.chksum = 0x379
after a while -  value1 = 0xBFEE, value2 = 0xD2FA, sum = 0x379, chksum = 0x379

Out of Reset - value1 = 0xBFEE, value2 = 0xD2FA, sum = 0x379, chksum = 0x379
setup new values -  value1 = 0xBFEF, value2 = 0xD2FB, sum = 0x37B, chksum = 0x379
updating chksum config.val.chksum = 0x37B
after a while -  value1 = 0xBFEF, value2 = 0xD2FB, sum = 0x37B, chksum = 0x37B

Out of Reset - value1 = 0xBFEF, value2 = 0xD2FB, sum = 0x37B, chksum = 0x37B
setup new values -  value1 = 0xBFF0, value2 = 0xD2FC, sum = 0x37D, chksum = 0x37B
updating chksum config.val.chksum = 0x37D
after a while -  value1 = 0xBFF0, value2 = 0xD2FC, sum = 0x37D, chksum = 0x37D

With this your variables will persist resets. As long as the power is not lost. Noting the above where you will get random data, to start.

Using either EEPROM or .noinit I would recommend that you checksum your persistent variable space during setup and if incorrect you can initialize or issue a warning.

mpflaga
  • 2,523
  • 14
  • 13
3

If you really need to update your NVRAM frequently, look into FRAM (Ferroelectric Random Access Memory) which claims a write life of 10^13 cycles (that's ~ 318,000 years if you write once/sec!). They're 8KB, and faster than Flash or EEPROM at 20MHz (SPI).

Adafruit (in the US) sells them on a breakout board for about US$6 in onesies. Or you can buy the bare chips from the usual suppliers for around US$1.30, but not DIP, only SOP8 packages.

JRobert
  • 15,407
  • 3
  • 24
  • 51
0

If you're open to a off-chip solution, it isn't too difficult to buy/build an SD card reader.

linhartr22
  • 606
  • 5
  • 10
0

I am very late to this party ... I just bought an UNO WiFi and I am looking at creating a simple protocol to persist data with a data store on another device connected to the WiFi. Probably will use HTTP with simple end points, treat the remote storage as a contiguous memory block:

GET IP:port/addr/len
PUT IP:port/addr data

I could also create named blocks (think files):

GET IP:port/name
PUT IP:port/name data

The actual remote storage could be anything, memory mapped disk file, SQLite3 database, etc.

There are many drawbacks, such as reliance on the external service, connectivity issues, error handling, etc. I could run out of code space just supporting the protocol.

EDIT: I got a little flack for my answer, including downvotes, however, read the OP - he stated "somewhere" for retrieving variable data; a LAN device certainly falls in the category of "somewhere", especially when the UNO has WiFi. My answer might not be suitable for the OP, however, after reading my answer, the OP might opt to get a WiFi enabled device. I do not believe in "keyhole" solutions, but rather to provide options that fit the general tone of the question even if it means getting out of its box.

user3481644
  • 109
  • 2
0

This was puzzling me too. I just wanted to use the onboard button to trigger a "count up an integer" regardless any setup routines. For me, mpflaga's answer gave me the right direction. But the given example was far too complicated for my needs. So here's the bare minimum example for an integer value that survives a reset, counting up. (But NOT a power loss, as it's in RAM!) Enjoy!

uint8_t c __attribute__ ((section (".noinit")));

void setup() { Serial.begin(9600); Serial.print("Just startet. c = "); Serial.print(c); if(c>7){c=0;}else{c++;} Serial.println(" - Waiting for RESET."); }

void loop() { // Do more stuff. }

awado
  • 113
  • 4