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
  • 411
  • 2
  • 3
  • 16
  • 1
    If you are only concerned about reboot, and not power cycling, then you can use RAM - you just have to keep any init code from wiping it. Of course if you need to survive power cycling too, then you need the EEPROM. – Chris Stratton May 17 '15 at 22:43
  • Actually I am really concerned about the power cycling. 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. – Samik Chattopadhyay May 19 '15 at 16:36

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,515
  • 1
  • 14
  • 26
  • I usually use for this type of things an external EEPROM; something like an AT24C02 works fine for most projects (it's 256 bytes).

    I just prefer external memory as it lasts more R/W cycles and because you can change it if it doesn't work anymore.

    – Stefa168 May 17 '15 at 21:00
  • 1
    The ram is not cleared. The global variables are set to zero according to the 'c' standard. The unused ram keeps its value during a reset. – Jot May 01 '17 at 18:25
  • Yes, you are right. I was thinking of a power failure which leads to a loss of the data in RAM (and which makes a reinitialization necessary). – fuenfundachtzig May 01 '17 at 20:04
  • what about to use sdcard reader. is not so much expensive and sdcard will live lot of time. – m3nda Jun 03 '18 at 14:49
  • adafruit seems to have FRAM for $5. Am i missing something? – rfii Jan 29 '21 at 04:05
  • Approx. 5.5 years? – fuenfundachtzig Jan 29 '21 at 07:23
  • I'm really late to this party ... the 100,000 (typical) writes before fail metric on the EEPROM - isn't that count on a per bit basis? Meaning, each bit in the EEPROM can be written as a '1' about 100,000 times before a failure is expected V writing ANYTHING ANYWHERE to the EEPROM? If the former, then a cumbersome write-count tracking system could be used by adding a write-count to each block of data, along with pointers to the current block. I did say "cumbersome", but it could work, like a disk directory. Cumbersome. – user3481644 Jun 10 '23 at 14:03
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,513
  • 13
  • 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,246
  • 3
  • 23
  • 51
2

Store them in the on-chip EEPROM.

Majenko
  • 105,095
  • 5
  • 79
  • 137
  • The EEPROM memory has a specified life of 100,000 write/erase cycles, so using EEPROM can be a limitation where program automatically writes values in it frequently, though, it reads them quite rare. – Samik Chattopadhyay May 17 '15 at 20:10
0

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

linhartr22
  • 596
  • 4
  • 10
  • 2
    Please try to provide a more thorough answer: provide links to such SD card readers, post some sample code on how to read/write variables from it, and mention how it is better than EEPROM. – jfpoilpret May 22 '15 at 04:27
  • Good suggestion. SD is using a regular SPI interface. So all you need is some logic-level-conversion. Beside that you wire them directly to an Arduino. Just solder some wires to a micro-SD to SD adapter. – Gerben May 27 '15 at 15:12
  • +jfpoilpret IMHO SD Card Readers are ubiquitous and shouldn't need examples or citation. The code is dependent on the brand of reader so no point in providing arbitrary code. I did mention how it is better than EEPROM, it isn't too difficult to buy or build. – linhartr22 May 28 '15 at 22:24
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
  • 2
    so your answer is "add WiFi and store the values on a remote server"? isn't that a little complicated if the project doesn't use WiFi for anything else? – Juraj Jun 23 '23 at 14:59
  • Does EEPROM not work for you? – Nick Gammon Jun 24 '23 at 08:46
  • @Juraj - Yes, that is my answer, and yes, it is complicated if your project does not use WiFi for anything else, but as I stated in my answer, WiFi is my best solution and I offered it as yet another possible answer for certain situations, see my response for Nick Gammon – user3481644 Dec 13 '23 at 16:01
  • @NickGammon - The EEPROM solution is not my best solution as I cannot predict how many updates to the persisted data, both in updates and new data, so I could eventually use all of the EEPROM data and/or perform too many writes to it, effectively destroying the UNO's capability for me. – user3481644 Dec 13 '23 at 16:03
  • All - the OP stated: "Is there any way to store the values of necessary variables somewhere" - emphasis on "somewhere" in my answer. "Somewhere" includes on a local network, which is what my answer provides. – user3481644 Dec 13 '23 at 16:04
  • Re-reading your answer, it seems to me like a new question. You might want to make it one. The question asked about remembering light switch statuses after a power failure. Given that the Uno has 1024 bytes of EEPROM, and only one bit is needed to remember a on/off status, you could store 8096 statuses. Or, do some load balancing, so that after 100,000 writes to EEPROM, you switch to a new bank of bytes. I'm assuming they don't have 8096 lights in their house. – Nick Gammon Dec 14 '23 at 17:19
  • Your suggestion of using a web server, for this purpose, would cause a long delay (maybe 5 minutes) while the router and web server rebooted and became ready to answer queries. If the power went off, would you really want to wait minutes before the lights came back on? If you are trying to solve a different problem than I want Arduino to remember what was on and what was off in my room after a power failure then I suggest you make your own, new, question. – Nick Gammon Dec 14 '23 at 17:21
  • @NickGammon I am using an Arduino with a very simplified implementation of HTTP, just so the client is familiar with the API. I've obtained a Rev 3 UNO with a off-brand SD/RTC board, using a simple HTTP service that I built for another project. The service is available in seconds. The router is a different issue, but I'm looking at simple devices, these will not be exposed to anything, so even a switch would be fine. – user3481644 Jan 01 '24 at 00:53
  • @NickGammon The whole thing with banks in EEPROM is a lot of monkeying around. I have 75+ devices, many of them are variable, so I would need 7 bits (0-99/128) for those. I also need a table to map devices to a slot in the memory bank. Many devices will be "hot" and be updated far more often, so I could lose a whole bank because of one or two devices. And then there is testing of the bank management - which is alleviated by just moving the bank with each write instead of counting, etc. However, I don't what to have to buy a new Arduino when one fails. – user3481644 Jan 01 '24 at 01:01
  • @NickGammon ... And ... My question extends beyond the scope of my question. I have similar projects (need for data) for my motor home. One application will have records that could be about 50 bytes each and an unknown number of records. Another application will have records with around 120 bytes each and there could be hundreds of those and any of those could be updated. – user3481644 Jan 01 '24 at 01:05
  • @NickGammon Not dwell on this, but in my house with the lights/devices, the router and server would be on a UPS (SPS) with about 12 hours of power, but the lights/devices are not. – user3481644 Jan 01 '24 at 01:06
  • You make some good points, but you are morphing the question from what the OP wanted "I want Arduino to remember what was on and what was off in my room after a power failure" to your requirements which seem somewhat different. If you want to make a new question with these somewhat more complex requirements then we could validly discuss it further. – Nick Gammon Jan 01 '24 at 07:50
  • @NickGammon My "morph" was explaining my project and how the OP might use my method for persisting data. Everyone else should have just left it at that - I was NEVER asking for help. – user3481644 Jan 04 '24 at 20:09
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