Writing a single member of a struct to EEPROM

I my sketch i do read and write my config to EEPROM doing this on the complete struct.
This is the struct:

struct	StoreStruct{
  long	_azAdZeroOffset;		// azAdZeroOffset value
  long	_elAdZeroOffset;		// elAdZeroOffset value
  long	_azScaleFactor;			// azimuth encoder scale factor
  long	_elScaleFactor;			// elevation encoder scale factor
  int	_closeEnough;			// value for catcing position
  long	_azimuthPark;			// Position where the antenna is parked after a timeout
  long	_elevationPark;			// 
  int	_rotationSpeed;			// Actual rotation speed
  int	_rotationSpeed4;		// Rotator speed High
  int	_rotationSpeed3;		//
  int	_rotationSpeed2;		//
  int	_rotationSpeed1;		// Rotator speed Low
  unsigned long	_rotorParkInterval;	// Duration after wich the rotors are parked
  char	softwareVersion[4];		// contains version number
}  
confValue = {
  0,
  0,
  3360L,                                // See documentation
  3360L,                                // See documentation
  25,                                   // Number of pulses to catch the rotorposition
  9000L,                                // Azimuth at 90 degrees
  9000L,                                // Elevation at 90 Degrees
  255,					// Actuel rotoation speed 100%
  254,                                  // Maximum speed at 254/255 = 99%
  192,                                  // Medium 1 speed at 192/255 = 75%
  127,                                  // Medium 2 speed at 127/255 = 50%
  63,                                   // Lowest speed at 64/255 = 25%
  90000,                                // Wait 90000*1mS before moving the dish to the parking position
  SOFTWARE_VERSION
};

And the EEPROM read and write are from the examples:

void loadConfig() { 
  lcdSerial << _BYTE(_line1) << "Read EEPROM ";
									// If there is no software version change it
									// will read the old config
  if (									// confValue.softwareVersion[3] is altijd 0
    EEPROM.read(sizeof(confValue) - 2) == confValue.softwareVersion[2] &&
    EEPROM.read(sizeof(confValue) - 3) == confValue.softwareVersion[1] &&
    EEPROM.read(sizeof(confValue) - 4) == confValue.softwareVersion[0])
  {									// reads settings from EEPROM
    for (unsigned int t=0; t<sizeof(confValue); t++) {
      *((char*)&confValue + t) = EEPROM.read(t);
      lcdSerial << _BYTE(160) << t;
      delay(150);
    }
  } 
  else {								// There is a software change so the new struct is
    saveConfig();							// written to the EEPROM
  }
  delay(250);
  lcdSerial << _BYTE(160) << "Done";
  delay(1500);
}

void  setMempoolEx()
{
//  EEPROM.setMemPool(memoryBase, EEPROMSizeATmega328);			//Set memorypool base to 32, assume Arduino Uno board
//  configAddress  = EEPROM.getAddress(sizeof(confValue));		// Size of config object 
}

boolean  loadConfigEx()
{
//  EEPROM.readBlock(configAddress, confValue);
}
void  saveConfigEx()
{
//  EEPROM.writeBlock(configAddress, confValue);  
}

void saveConfig()
{
  lcdSerial << _BYTE(_line1) << "Write Conf. ";

  for(unsigned int t=0; t<sizeof(confValue); t++)
  {// Write to EEPROM
    EEPROM.write(t, *((char*)&confValue + t));
    lcdSerial << _BYTE(160) << t;
    delay(100);
    if (EEPROM.read(t) != *((char*)&confValue + t))
    {
      Serial << "Error writing to EEPROM-" << t << endl;
    }
  }
}

But sometimes i change only change ’ int _rotationSpeed; // Actual rotation speed’.
As my C++ skils are very rudimentary i am not quit sure if this is possible.
Anny help would be welcome.

You can calculate the relative address of that field by subtracting the address of the field from the address of the struct, and determine its size using sizeof(), and then write the relevant bytes to the appropriate offset into the EEPROM.

Something like:

//incomplete, untested

int offset = &confValue._rotationSpeed - &confValue; 
int size = sizeof(confValue._rotationSpeed);
byte *conf = &confValue;

for(int i = offset; i < (offset + size); i++)
{
  EEPROM.write(i, conf[i]);
}

As i told before i am a complete noob to C++ and what you are proposing is total jiberisch to mee. I have tried using the serial output to get some figures that would make sense to me but no luck. I know how to get the size of the array being 44 for now [sizeoff(confValue)]

Then i try to get the pointer to the _rotorSpeed: int _rotorSpeedPointer; _rotorSpeedPointer = ((int)&confValue._rotorSpeed);

This gives me trhe value of 815. But i think i stl missing something

gharryh: I my sketch i do read and write my config to EEPROM doing this on the complete struct.

http://playground.arduino.cc/Code/EEPROMWriteAnything

[quote author=Nick Gammon link=topic=152327.msg1144339#msg1144339 date=1362519491]

gharryh: I my sketch i do read and write my config to EEPROM doing this on the complete struct.

http://playground.arduino.cc/Code/EEPROMWriteAnything [/quote] I am aware of this, but saving the extra write cycles on the EEPROM i wanted to update just one member of that struct to EEPROM

gharryh: But i think i stl missing something

Have you tried the code I gave you?

PeterH gave you code to write to part of the struct, plus I don't see why you couldn't use the EEPROMWriteAnything to access part of the struct.

[quote author=Nick Gammon link=topic=152327.msg1144566#msg1144566 date=1362535488] PeterH gave you code to write to part of the struct, plus I don't see why you couldn't use the EEPROMWriteAnything to access part of the struct. [/quote] I tried to use Peter's code but it gave only error's. In my humble opinion EEPROManything can only read or write complete structures as can be seen in the example at the webpage:http://playground.arduino.cc/Code/EEPROMWriteAnything Maybe i am to old or to stupid for this but i realy do need more to solve this. Maybe its becourse my native language is dutch an so i dont know how to explain the problem in writing english.

gharryh: I tried to use Peter's code but it gave only error's.

I haven't tried compiling or testing it myself, but I doubt that it was far off based on what I could see of your code. What were the errors? Have you tried to fix them?

In my humble opinion EEPROManything can only read or write complete structures as can be seen in the example at the webpage:http://playground.arduino.cc/Code/EEPROMWriteAnything

No, that is not true. It can write data of any type. Sometimes, the most convenient way to write more than one value is to put the values in a struct, and write the whole struct at once.

That is not the only way to write or read anything from EEPROM. If you want to write a single unsigned long, pass the function an unsigned long.

For now i have solved the problem like this:

void saveSpeed()
{
  int posValue = 26; // this is the position in EEPROM. Got it from a EEPROM dump
  for(unsigned int t = 0; t < sizeof(confValue._rotationSpeed); t++)
  {
    EEPROM.write(t + 26, confValue._rotationSpeed);
  }
  Serial << "Setting saved" << endl;
}

The disadvantice of this is that i have to remember to change the posValue everytime i change something in the structure

The disadvantice of this is that i have to remember to change the posValue everytime i change something in the structure

Peter's code in reply #1 showed how to determine the start position of confValue rather than setting it manually. Did you ever try it ?

This line gives a error:

int offset = &confValue._rotationSpeed - &confValue;

Error:

  • EEPROM.ino: In function ‘void saveSpeed()’:
    EEPROM:46: error: invalid operands of types ‘byte*’ and ‘StoreStruct*’ to binary ‘operator-’
    EEPROM:48: error: cannot convert ‘StoreStruct*’ to ‘byte*’ in initialization

And the error tells me nothing, maybe other people do.
Playing around with * and & doesnt help either.
Serial << "Value " << _DEC(confValue._azAdZeroOffset) << endl; gives 192 and that is what the location contains

Serial << "Start " << _DEC(&confValue._azAdZeroOffset) << endl; gives a error and not the address value of this location.
The error - EEPROM:49: error: invalid conversion from ‘long int*’ to ‘long int’
EEPROM:49: error: initializing argument 1 of ‘_BASED::_BASED(long int, int)’

I expect you need to cast the address of the struct to a byte pointer before you do the subtraction.

PeterH:
I expect you need to cast the address of the struct to a byte pointer before you do the subtraction.

Do you mean somthing like this(get a error as wel)

byte confStart = &confValue._azAdZeroOffset;
Serial << "Start " << _DEC(confStart) << endl;

gharryh: The disadvantice of this is that i have to remember to change the posValue everytime i change something in the structure

You can do it like this:

#define SOFTWARE_VERSION "1.1"

struct  StoreStruct{
  long _azAdZeroOffset;        // azAdZeroOffset value
  long _elAdZeroOffset;        // elAdZeroOffset value
  long _azScaleFactor;         // azimuth encoder scale factor
  long _elScaleFactor;         // elevation encoder scale factor
  int  _closeEnough;           // value for catcing position
  long _azimuthPark;           // Position where the antenna is parked after a timeout
  long _elevationPark;         // 
  int  _rotationSpeed;         // Actual rotation speed
  int  _rotationSpeed4;        // Rotator speed High
  int  _rotationSpeed3;        //
  int  _rotationSpeed2;        //
  int  _rotationSpeed1;        // Rotator speed Low
  unsigned long    _rotorParkInterval; // Duration after wich the rotors are parked
  char softwareVersion[4];     // contains version number
}  
confValue = {
  0,
  0,
  3360L,                                // See documentation
  3360L,                                // See documentation
  25,                                   // Number of pulses to catch the rotorposition
  9000L,                                // Azimuth at 90 degrees
  9000L,                                // Elevation at 90 Degrees
  255,                 // Actuel rotoation speed 100%
  254,                                  // Maximum speed at 254/255 = 99%
  192,                                  // Medium 1 speed at 192/255 = 75%
  127,                                  // Medium 2 speed at 127/255 = 50%
  63,                                   // Lowest speed at 64/255 = 25%
  90000,                                // Wait 90000*1mS before moving the dish to the parking position
  SOFTWARE_VERSION
};

// find the offset of a  member inside a structure
#define offsetof(s,m) (size_t)&(((s *)NULL)->m)

void setup ()
  {
  Serial.begin (115200);
  size_t rotationSpeedOffset = offsetof (StoreStruct, _rotationSpeed);
  size_t rotationSpeedLength = sizeof (confValue._rotationSpeed);
  Serial.print   ("Offset is ");  
  Serial.println (rotationSpeedOffset);
  Serial.print   ("Length is ");  
  Serial.println (rotationSpeedLength);
  }  // end of setup

void loop () { }

Output:

Offset is 26
Length is 2

Notice how the 26 agrees with yours, but it is calculated by the compiler.

gharryh:

PeterH:
I expect you need to cast the address of the struct to a byte pointer before you do the subtraction.

Do you mean somthing like this(get a error as wel)

byte confStart = &confValue._azAdZeroOffset;

Serial << "Start " << _DEC(confStart) << endl;

I have no idea what that’s meant to do. No. I mean that the compiler might want to you cast the pointer values to the correct type.

For example:

byte *conf = &confValue;

Probably should be:

byte *conf = (byte *) &confValue;

I suggest you ignore any lines that start with Serial << since they are not relevant to your problem; if you can’t get them to compile that’s a separate problem unrelated to the EEPROM updates.

Given the structure above, this is how you might stream the results:

#include <Streaming.h>

// find the offset of a  member inside a structure
#define offsetof(s,m) (size_t)&(((s *)NULL)->m)

void setup ()
  {
  Serial.begin (115200);
  Serial << "Offset is " << offsetof (StoreStruct, _rotationSpeed)
         << ", Length is " << sizeof (confValue._rotationSpeed) << endl;
  }  // end of setup

void loop () { }

Output:

Offset is 26, Length is 2

The Serial statement are for debugging only.
@Nick using your solution works now. Maybe lateron i can rewrite it to be more universal if i need more valua’s te be saved on different places.