Need explanation for EEPROM usage (generally and on Photon)

I’m lacking some basic understanding on how the EEPROM on the Photon can be used. As I understand there are 2047 bytes of date accessible as permanent storage. Every byte can be addressed by an address as an integer. What can I store in one byte under one address? One byte is 8 bit I guess - so can I store a number from 0 to 255? If correct what do I do if I want to store bigger numbers or strings with more than one character. Can I store characters?

If I read/write access the storage address as I understand it I store there “pure data”, so I could not actually store a letter but only …yes what a number coding for the letter?

If I use the function storing an object instead of directly accessing data, what would be the practical difference?

Would appreciate if someone could give me a hint.

Sebastian

Here’s an example of reading and writing all sorts of data into EEPROM.

// EEPROM sample code
// EEPROM Documentation: https://docs.particle.io/reference/firmware/photon/#eeprom

#include "Particle.h"

void clearEEPROM(); // forward declaration

// This is just a list of 16 fish names from Wikipedia for testing
const char *fishNames[] = {"Aeneus corydoras", "African glass catfish", "African lungfish", "Aholehole",
						   "Airbreathing catfish", "Airsac catfish", "Alaska blackfish", "Albacore",
						   "Alewife", "Alfonsino", "Algae eater", "Alligatorfish",
						   "Alligator gar", "American sole", "Amur pike", "Anchovy"};

typedef struct {
	int a;
	float b;
	bool c;
} SimpleStruct;

void setup() {
	Serial.begin(9600);

	// clearEEPROM();
}


void loop() {
	int addr = 0;
	int intVal;

	// int
	{
		// You can get and put simple values like int, long, bool, etc. using get and put directly

		EEPROM.get(addr, intVal);
		Serial.printlnf("addr=%d, intVal=%d, sizeof(int)=%d", addr, intVal, sizeof(int));

		intVal++;
		EEPROM.put(addr, intVal);

		addr += sizeof(int);
	}
	// double
	{
		double doubleVal;

		// Same for float, double
		EEPROM.get(addr, doubleVal);
		Serial.printlnf("addr=%d, doubleVal=%lf, sizeof(doubleVal)=%d", addr, doubleVal, sizeof(doubleVal));

		doubleVal += 0.1;
		EEPROM.put(addr, doubleVal);

		addr += sizeof(doubleVal);
	}

	// Strings are a bit more of a pain because you have to know how much space you want to reserve.
	// In this example, we store a string of up to 15 characters, plus a null byte, in a 16 character buffer
	{
		const int STRING_BUF_SIZE = 16;
		char stringBuf[STRING_BUF_SIZE];

		EEPROM.get(addr, stringBuf);
		stringBuf[sizeof(stringBuf) - 1] = 0; // make sure it's null terminated

		// Initialize a String object from the buffer
		String str(stringBuf);

		Serial.printlnf("addr=%d, str=%s, sizeof(stringBuf)=%d", addr, str.c_str(), sizeof(stringBuf));

		str = String(fishNames[intVal & 0xf]);
		Serial.printlnf("next fish name=%s", str.c_str());

		// getBytes handles truncating the string if it's longer than the buffer.
		str.getBytes((unsigned char *)stringBuf, sizeof(stringBuf));
		EEPROM.put(addr, stringBuf);

		addr += sizeof(stringBuf);
	}

	// A simple structure
	{
		SimpleStruct data;

		// You can even store a small structure of values
		EEPROM.get(addr, data);
		Serial.printlnf("addr=%d, a=%d b=%f c=%d, sizeof(data)=%d", addr, data.a, data.b, data.c, sizeof(data));

		data.a += 2;
		data.b += 0.02;
		data.c = !data.c;

		EEPROM.put(addr, data);

		addr += sizeof(data);
	}
	Serial.println("--------");

	delay(30000);
}

void clearEEPROM() {
	for(int addr = 0; addr < 256; addr++) {
		EEPROM.write(addr, 0);
	}
}

And the sample output from running it:

addr=0, intVal=0, sizeof(int)=4
addr=4, doubleVal=0.000000, sizeof(doubleVal)=8
addr=12, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
addr=28, a=0 b=0.000000 c=0, sizeof(data)=12
--------
addr=0, intVal=1, sizeof(int)=4
addr=4, doubleVal=0.100000, sizeof(doubleVal)=8
addr=12, str=African glass c, sizeof(stringBuf)=16
next fish name=African lungfish
addr=28, a=2 b=0.020000 c=1, sizeof(data)=12
--------
addr=0, intVal=2, sizeof(int)=4
addr=4, doubleVal=0.200000, sizeof(doubleVal)=8
addr=12, str=African lungfis, sizeof(stringBuf)=16
next fish name=Aholehole
addr=28, a=4 b=0.040000 c=0, sizeof(data)=12
--------
addr=0, intVal=3, sizeof(int)=4
addr=4, doubleVal=0.300000, sizeof(doubleVal)=8
addr=12, str=Aholehole, sizeof(stringBuf)=16
next fish name=Airbreathing catfish
addr=28, a=6 b=0.060000 c=1, sizeof(data)=12
--------
6 Likes

I do hear some misconception here.
A binary processor does not differenciate between numbers or letters, hex or dec numbers or such - it only deals with bits and collections of them, which we humans connotate with some kind of human-readable representation.
So storing a byte number or a char letter is the same for the processor, we only tell the compiler that we want to see the one collection of bits as number and the other as letter.

And as a next step, if you want to store strings or bigger numbers or such, that’s also just a collection of bytes stored in sequence starting at one byte address and taking up the space of multiple individually addressable bytes (which we don’t intend to read on their own).

For storing and reading back these compounds the docs tell about
EEPROM.put() and EEPROM.get()

2 Likes

Thank you both for the example and explanation. For my program I will use the get and put methods. If I would use the write method what do I actually store under the byte address? In the reference example:

int addr = 1;
uint8_t val = 0x45;
EEPROM.write(addr, val);

The “0x45” is that hexadecimal representation of the data stored? Do I have to use hexadecimal?

If I use the put method. The library will take care of how many bytes will be used. But do i have to consider the length of my data? In other words if I want to store two objects wich are larger than 1 byte I could not use 0 and 1 as adresses for the two objects because otherwise the second will be written partly on top of the first, correct?

As said, hex is only for us humans to understand it. If you write 0x45 or 69 or ‘E’ does not matter.

For the length of your data you can use the sizeof(firstVar) operator to calculate the next base address.

How can we store IPAddress values in EEPROM?

What have you tried?
In what way did it not work?
If you haven’t tried anything, may I encourage you to do?

This might be a good place to start from
https://docs.particle.io/reference/device-os/firmware/photon/#put-

Well I did try it the direct way where I pass it the IP on an address. However, when I try to read it, the photon goes into SOS mode.

You can try this

  EEPROM.put(addr, ip.raw());
  EEPROM.get(addr, ip.raw());

As an alternate to try - this library abstracts some of the concepts and while you could be more space efficient, it does provide a simple approach to managing data in records. I use out all the time to hold configuration information on my devices.

Hello,
Can someone help me understand the limitation in the available permanent memory? As the device for example electron has 1Mb storage capacity but the usable permanent space is getting reduced to 2047 bytes. Can this be increased somehow or it is a limitation from hardware point of view?

Regards,
Prudhvi Sagar

First you may want to have a look at the memory map how the 1MB are divided into multiple sections.
And then from that
image
plus the documentation about EEPROM you should be able to see why you only get 2K usable EEPROM space (due to the need for wear leveling).

If you need more data stored you could use low level access to the EEPROM emulation banks and place your data there directly, but this is not recommended nor officially supported.