Go Down

Topic: EEPROM.put() -- floats and loops? (Read 5214 times) previous topic - next topic

sucka

Mar 28, 2016, 08:13 pm Last Edit: Mar 28, 2016, 08:44 pm by sucka
So I don't know what gives here. I've been banging my head against a wall trying to figure out why my code does not write floats properly to EEPROM of my arduino UNO. Rather, it will write one value at the end of a for loop and all other values come out as garbage or negative values, or "-0.00"?
 




Code: [Select]
#include "Arduino.h"
#include <EEPROM.h>




int eLocation = 0;
float ePutValue = 01.45;
float eGetValue;


void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Serial.println("");
  Serial.println("");
  Serial.println("---------STARTING-----------");




  //to do the get method--->EEPROM.get(address, data)
  Serial.println("Last Value Known:");
  EEPROM.get(eLocation, eGetValue);
  Serial.println(eGetValue, 2);
  Serial.println("");
  Serial.println("");


  // get values in a for loop
  for (int i = 1; i < 5; i++) {
    Serial.println("Loop Value:");
    EEPROM.get(eLocation, eGetValue);
    Serial.println(eGetValue, 2);
    eLocation = eLocation + 1;
    Serial.println("");
    delay(200);
  }




  Serial.println("");
  Serial.println("");
  Serial.println("***********Writing*************");




  //write values in a for loop
  for (int i = 1; i < 5; i++) {

    Serial.println("Write Value:");
    //delay(500);              //pause at least 3.3ms to write data.
    //One simple call for put, with the address first and the object second.
    EEPROM.put(i, ePutValue);
    Serial.println(ePutValue);

    Serial.println("To address:");
    Serial.println(i);
    Serial.println("");
    delay(500);
    ePutValue = ePutValue + 1.12;
  }



  Serial.println("");
  Serial.println("");
  Serial.println("**************Final Readout***************:");


  for (int i = 0; i < 5; i++) {

    Serial.println("get method:");
    EEPROM.get(i, eGetValue);
    Serial.println(eGetValue);
    Serial.println("");

  }


} //end setup()

void loop() {
  // put your main code here, to run repeatedly:

}



The final readout values are all -0.00 except for the last one, which is 1.45(which is correct).
Can anyone tell me what I'm doing wrong? Why is only the last value being correctly written here? Upon resetting and checking the serial monitor, none of the correct values are being returned from EEPROM. I'm probably missing something basic. HELP!

-dan
 

BigBobby

#1
Mar 28, 2016, 09:04 pm Last Edit: Mar 28, 2016, 09:05 pm by BigBobby
Floats are 4 bytes long.  When you are writing to different EEPROM addresses, you are only incrementing by 1 so each float overwrites 3 of the bytes of the previous float.

To fix this you need to replace all of the places that you incremented the EEPROM address by 1 with 4 (or better yet, sizeof(eGetValue)).

This code:
Code: [Select]

#include "Arduino.h"
#include <EEPROM.h>




int eLocation = 0;
float ePutValue = 01.45;
float eGetValue;


void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Serial.println("");
  Serial.println("");
  Serial.println("---------STARTING-----------");




  //to do the get method--->EEPROM.get(address, data)
  Serial.println("Last Value Known:");
  EEPROM.get(eLocation, eGetValue);
  Serial.println(eGetValue, 2);
  Serial.println("");
  Serial.println("");


  // get values in a for loop
  for (int i = 1; i < 5; i++) {
    Serial.println("Loop Value:");
    EEPROM.get(eLocation, eGetValue);
    Serial.println(eGetValue, 2);
    eLocation = eLocation + sizeof(eGetValue);
    Serial.println("");
    delay(200);
  }




  Serial.println("");
  Serial.println("");
  Serial.println("***********Writing*************");




  //write values in a for loop
  for (int i = 1; i < 5; i++) {

    Serial.println("Write Value:");
    //delay(500);              //pause at least 3.3ms to write data.
    //One simple call for put, with the address first and the object second.
    EEPROM.put(i * sizeof(ePutValue), ePutValue);
    Serial.println(ePutValue);

    Serial.println("To address:");
    Serial.println(i);
    Serial.println("");
    delay(500);
    ePutValue = ePutValue + 1.12;
  }



  Serial.println("");
  Serial.println("");
  Serial.println("**************Final Readout***************:");


  for (int i = 0; i < 5; i++) {

    Serial.println("get method:");
    EEPROM.get(i * sizeof(eGetValue), eGetValue);
    Serial.println(eGetValue);
    Serial.println("");

  }


} //end setup()

void loop() {
  // put your main code here, to run repeatedly:

}


Results in this:
Quote
---------STARTING-----------
Last Value Known:
ovf


Loop Value:
ovf

Loop Value:
1.45

Loop Value:
2.57

Loop Value:
3.69



***********Writing*************
Write Value:
1.45
To address:
1

Write Value:
2.57
To address:
2

Write Value:
3.69
To address:
3

Write Value:
4.81
To address:
4



**************Final Readout***************:
get method:
ovf

get method:
1.45

get method:
2.57

get method:
3.69

get method:
4.81

sucka

Perfect! Simple answer, staring me in the face, as always. Though have to say I'm surprised this kind of stuff isn't abstracted away from the user, expecially for noobs like myself. Something like an extra parameter in the EEPROM.put() function, like EEPROM.put(type, address, value).  Where "type" is float, double, int, etc.  The .get() function then could just autodetect the type and output the val.  Then again, what the hell do I know..thanks again! 











BigBobby

#3
Mar 29, 2016, 12:39 am Last Edit: Mar 29, 2016, 12:41 am by BigBobby
Perfect! Simple answer, staring me in the face, as always. Though have to say I'm surprised this kind of stuff isn't abstracted away from the user, expecially for noobs like myself. Something like an extra parameter in the EEPROM.put() function, like EEPROM.put(type, address, value).  Where "type" is float, double, int, etc.  The .get() function then could just autodetect the type and output the val.  Then again, what the hell do I know..thanks again!
Well, overloaded methods prevent you from needing a "type" argument.  The compiler knows the type of value is a float, so it then calls the appropriate method to write a 4 byte float to the EEPROM.  It can't really make any assumptions about addresses, however, as you could very well want to store your float at addresses 1-4 as opposed to addresses 0-3.

If you want to abstract this stuff away, however, you could define a variable as EEMEM and then let the compiler handle the addresses for you:
Code: [Select]

#include "Arduino.h"
#include <EEPROM.h>

#define NUM_ELOCATIONS 5

float ePutValue = 01.45;
float eGetValue;
EEMEM float eLocation[NUM_ELOCATIONS];

void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Serial.println(F("***********Writing*************"));

  //write values in a for loop
  for (int i = 0; i < NUM_ELOCATIONS; i++) {

    Serial.println(F("Write Value:"));
    EEPROM.put((int)(&eLocation[i]), ePutValue);
    Serial.println(ePutValue);

    Serial.println("To address:");
    Serial.println((int)(&eLocation[i]));
    Serial.println();
    ePutValue = ePutValue + 1.12;
  }



  Serial.println(F("**************Final Readout***************:"));


  for (int i = 0; i < NUM_ELOCATIONS; i++) {

    Serial.println(F("get method:"));
    EEPROM.get((int)(&eLocation[i]), eGetValue);
    Serial.println(eGetValue);
    Serial.println("");

  }

} //end setup()

void loop() {
  // put your main code here, to run repeatedly:

}


Note that this method is a bit dangerous, however, as you likely want to ensure that your EEPROM variables are always assigned to the same locations.  For example, in your first revision of code you might have 1 variable in EEPROM that gets assigned to address 0.  You then create a new revision of code revision of code that has 2 variables in EEPROM.  You don't want the compiler to declare the new variable to have address 0!  The best way to handle this is to put all of your EEPROM variables into a structure to force the compiler to put them in the same order.

Code: [Select]

EEMEM struct
{
  float eLocation[NUM_ELOCATIONS];
  // put all of your new variables after the first.
} eeData;

marco_c

One simple way around this that I use (and it always works) is to package up all the EEPROM data one or more structures and get/put the structure in one hit. I generally use EEPROM data for configuration information and this also has the side benefit of clearly identifying the config data in the code.
Arduino Libraries https://github.com/MajicDesigns?tab=Repositories
Parola for Arduino https://github.com/MajicDesigns/Parola
Arduino++ blog https://arduinoplusplus.wordpress.com

sucka

Great explanations, thank you again for the follow up and for the feedback. 

Go Up