Go Down

Topic: Writing float values to Eeprom (Read 17829 times) previous topic - next topic

GordonEndersby

Hi guys,

Ive learnt a lot on the forum just by reading all the posts.

But now I have a little c question.
Im trying to save some temperature values to the onboard Eeprom.
I was using a float array to store the values.
But changed it to double to fit the example from:
http://www.openobject.org/opensourceurbanism/Storing_Data
It looks right and I assume hes had it working but Im getting an error when I build the code.
My code is below.

In function 'void EEPROMWriteDouble(int, double)':
error: invalid operands of types 'double' and 'int' to binary 'operator>>

So its telling me that I cant use the binary operator on the double p_value.

My question is how can I split a double or float to its 4 bytes to store on the eeprom?

Ive tried changing from double to long, binary operators work, but I lose the values after the decimal places.
Can I preserve the whole value or am I going to have to multiply the value by 100 to fit the long?
Theres no saving in bytes on the eeprom by using long as its still 4 bytes.

I hope that lot makes sense.

Thanks

Gordon

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

double testInt[12] = {
 -12.5, -10.00, -5.7, 0, 2.45, 2.90, 3.10, 4 , 5.6, 7.9, 5.5, 4};
byte noElem = 12;
unsigned int baseAddr = 0;

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

 // write float array to eeprom
 // float value takes 4 bytes
 // addresses are baseAddr + address every 4 bytes
 for (int i=0; i < noElem-1; i++){
   EEPROMWriteDouble( (i*4)+baseAddr, testInt[i]);
 }

 //read data back
 for (int i=0; i < noElem-1; i++){
   int addr = (i*4)+baseAddr;
   double val = EEPROMReadDouble( addr);
   Serial.print((long)val);
 }
}

void loop() {

}

void EEPROMWriteDouble(int p_address, double p_value)
{
 byte Byte1 = ((p_value >> 0) & 0xFF);
 byte Byte2 = ((p_value >> 8) & 0xFF);
 byte Byte3 = ((p_value >> 16) & 0xFF);
 byte Byte4 = ((p_value >> 24) & 0xFF);

 EEPROM.write(p_address, Byte1);
 EEPROM.write(p_address + 1, Byte2);
 EEPROM.write(p_address + 2, Byte3);
 EEPROM.write(p_address + 3, Byte4);
}

float EEPROMReadDouble(int p_address)
{
 byte Byte1 = EEPROM.read(p_address);
 byte Byte2 = EEPROM.read(p_address + 1);
 byte Byte3 = EEPROM.read(p_address + 2);
 byte Byte4 = EEPROM.read(p_address + 3);

 long firstTwoBytes = ((Byte1 << 0) & 0xFF) + ((Byte2 << 8) & 0xFF00);
 long secondTwoBytes = (((Byte3 << 0) & 0xFF) + ((Byte4 << 8) & 0xFF00));
 secondTwoBytes *= 65536; // multiply by 2 to power 16 - bit shift 24 to the left

 return (firstTwoBytes + secondTwoBytes);
}

mem

The safest thing would be to multiply the value by 100 or whatever to convert it into a long value.

If you really wanted to get dirty and retrieve the byte values you could use a union. Something like the following would return the double value as if it was a long.  

long doubleToLong(double value) {
 union { double d; long l; } n = {value};
 return n.d;
}

halley

The safest thing would be to treat the variable as a byte array in RAM, and transfer it byte-for-byte into EEPROM.

Untested.

Code: [Select]
void EEPROM_writeDouble(int ee, double value)
{
   byte* p = (byte*)(void*)&value;
   for (int i = 0; i < sizeof(value); i++)
       EEPROM.write(ee++, *p++);
}

double EEPROM_readDouble(int ee)
{
   double value = 0.0;
   byte* p = (byte*)(void*)&value;
   for (int i = 0; i < sizeof(value); i++)
       *p++ = EEPROM.read(ee++);
   return value;
}

halley

#3
Feb 13, 2009, 12:32 am Last Edit: Feb 13, 2009, 12:36 am by halley Reason: 1
Okay, I decided to go ahead and write the general templated functions and test them.  Read anything.  Write anything.  Each function returns the number of EEPROM bytes read or written to accomplish the data transfer.  Woo hoo.

Code: [Select]

#include <EEPROM.h>

template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
   const byte* p = (const byte*)(const void*)&value;
   int i;
   for (i = 0; i < sizeof(value); i++)
       EEPROM.write(ee++, *p++);
   return i;
}

template <class T> int EEPROM_readAnything(int ee, T& value)
{
   byte* p = (byte*)(void*)&value;
   int i;
   for (i = 0; i < sizeof(value); i++)
       *p++ = EEPROM.read(ee++);
   return i;
}


For testing and demonstrations:

Code: [Select]

// include the above EEPROM functions

struct st_t { long lo; byte by; double db; };

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

   int n = 0;

   byte by = 0x33;
   Serial.println(by, HEX);
   n = EEPROM_writeAnything(0, by);
   Serial.print("Wrote bytes: ");
   Serial.println(n);
   by = 0xFF;
   Serial.println(by, HEX);
   n = EEPROM_readAnything(0, by);
   Serial.print("Read bytes: ");
   Serial.println(n);
   Serial.println(by, HEX);

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

   long lo = 0xDEADBEEF;
   Serial.println(lo);
   n = EEPROM_writeAnything(23, lo);
   Serial.print("Wrote bytes: ");
   Serial.println(n);
   lo = 0xFFFFFFFF;
   Serial.println(lo);
   n = EEPROM_readAnything(23, lo);
   Serial.print("Read bytes: ");
   Serial.println(n);
   Serial.println(lo);

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

   double pi = 3.1415926538;

   struct st_t st;
   st.lo = 0xABADF00D;
   st.by = 0x22;
   st.db = pi;
   Serial.println(st.lo);
   Serial.println(st.by, HEX);
   Serial.println(st.db == pi);
   n = EEPROM_writeAnything(221, st);
   Serial.print("Wrote bytes: ");
   Serial.println(n);
   st.lo = 0xFFFFFFFF;
   st.by = 0x11;
   st.db = 0.0;
   Serial.println(st.lo);
   Serial.println(st.by, HEX);
   Serial.println(st.db == pi);
   n = EEPROM_readAnything(221, st);
   Serial.print("Read bytes: ");
   Serial.println(n);
   Serial.println(st.lo);
   Serial.println(st.by, HEX);
   Serial.println(st.db == pi);
}

void loop()
{
}

mikalhart

Ok, I have to say that's pretty sweet. :)

Way to go template man.  Woo hoo! indeed.

Mikal

kg4wsv

Nice!

wrap this up as a mod to the standard EEPROM library and send it in for 0014?

-j


GordonEndersby


Thanks guys,
Thats been really useful and Halley's code looks like it would be useful on the eeprom page in the playground or rolled into the eeprom library.

Halley,
I can follow what youve written except:
   struct st_t st;
   st.lo = 0xABADF00D;
   st.by = 0x22;
   st.db = pi;
Which I assume is to change the double to a form that can be used by the template.
and:
   st.lo = 0xFFFFFFFF;
   st.by = 0x11;
   st.db = 0.0;
a form for the that is returned by the template.

From my googling this is a symbol table?
Which is some deep and dark magic in c that I havnt come across yet.
Would you mind explaining what it does and the meaning of the values being assigned.
Ive done some looking up on the net but cant relate what Im finding to the struct youve used.

Thanks

Gordon


halley

#7
Feb 13, 2009, 01:56 pm Last Edit: Feb 13, 2009, 01:58 pm by halley Reason: 1
Gordon, the last third of the test is to develop some arbitrary data structure, a struct.  It has three members of three different types, for a total of 9 bytes (when packed on byte boundaries).  Read up on the struct keyword in any C book or tutorial.

I set the double member (st.db) to a known value, so I could compare the values exactly.  Comparing floating-point numbers for exact equivalence after a calculation is not a good idea but this test should ensure we are absolutely accurate and equal in every bit of the variable.

The middle part of that same test, where I set things to 0xFFFFFFF, 0x11 and 0.0, is just to erase the structure's values with different contents, so that we can prove that the EEPROM_readAnything() call really is restoring the contents from other memory.

GordonEndersby


Thanks for replying again Halley.
Im familiar with struct
Its was the type youve used st_t and its members lo and by I cant find any info on.
I realise now you used it as an example and its not key to saving the data to the eeprom.

Ill have a good play later and see how I get on.

Thanks
Gordon


GordonEndersby



AAAAHHHHHHHH!!!!!!!!!

Ive just re read it properly and found the line:
struct st_t { long lo; byte by; double db; };

Theres me thinking it was some strange built in c black magic.
So no wonder when I found references to st_t and symbol tables through google it didnt make any sense.

Gordon

GordonEndersby

Works great and is easy to use.
Thanks Halley.

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


template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
   const byte* p = (const byte*)(const void*)&value;
   int i;
   for (i = 0; i < sizeof(value); i++)
       EEPROM.write(ee++, *p++);
   return i;
}

template <class T> int EEPROM_readAnything(int ee, T& value)
{
   byte* p = (byte*)(void*)&value;
   int i;
   for (i = 0; i < sizeof(value); i++)
       *p++ = EEPROM.read(ee++);
   return i;
}


double testInt[12] = { -12.5, -10.00, -5.7, 0, 2.45, 2.90, 3.10, 4 , 5.6, 7.9, 5.5, 4};
byte noElem = 12;
unsigned int baseAddr = 0;
unsigned int n = 0;

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

 // write data to eeprom
 for (int i=0; i <= noElem-1; i++){
   n = EEPROM_writeAnything( (i*4)+baseAddr, testInt[i]);
 }

 // read data back
 for (int i=0; i <= noElem-1; i++){
   double val;
   int addr = (i*4)+baseAddr;
   n = EEPROM_readAnything( addr, val);
   Serial.println(val);
 }
}

void loop() {
}

halley

#11
Feb 13, 2009, 07:08 pm Last Edit: Feb 13, 2009, 07:12 pm by halley Reason: 1
Glad you got it working, Gordon.  I'm going to change your code a bit.  When writing solid code, it's good not to compute the same thing in two different ways.  The goal of returning bytes-written is to let you write multiple things easily.

Code: [Select]
// ... all the EEPROM stuff

[glow]// countof(array) gives number of declared elements
#define countof(a)  (sizeof(a) / sizeof(*a()))[/glow]

double testInt[12] = { -12.5, -10.00, -5.7, 0, 2.45, 2.90, 3.10, 4 , 5.6, 7.9, 5.5, 4};
[glow]// byte noElem = 12; // not needed[/glow]
unsigned int baseAddr = 0;
[glow]// unsigned int n = 0; // use a local variable for this[/glow]

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

 // write data to eeprom
 int addr = baseAddr;
 for (int i=0; i < [glow]countof(testInt)[/glow]; i++) {
   [glow]int[/glow] n = EEPROM_writeAnything([glow]addr[/glow], testInt[i]);
   [glow]addr += n;[/glow]
 }

 // read data back
 [glow]addr = baseAddr;[/glow]
 for (int i=0; i < [glow]countof(testInt)[/glow]; i++){
   double val;
   [glow]int[/glow] n = EEPROM_readAnything(addr, val);
   [glow]addr += n;[/glow]
   Serial.println(val);
 }
}

void loop() {
}


It's even simpler than that.  This might bend your noodle, but the countof() trick may have given you a clue... the "variable" that you pass does not have to be a single element.  A whole array is still just one variable, so you can read or write the whole array in one call.

Code: [Select]
// ... all the EEPROM stuff

[glow]// countof(array) gives number of declared elements
#define countof(a)  (sizeof(a) / sizeof(*a()))[/glow]

double testInt[12] = { -12.5, -10.00, -5.7, 0, 2.45, 2.90, 3.10, 4 , 5.6, 7.9, 5.5, 4};
unsigned int baseAddr = 0;

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

 // write data to eeprom all at once
 [glow]n = EEPROM_writeAnything(baseAddr, testInt[/glow]);

 // read data back in separate temporary buffer
 [glow]double scratch[countof(testInt)];
 int n = EEPROM_readAnything(baseAddr, scratch);
 for (int i = 0; i < countof(scratch); i++)
   Serial.println(scratch[i]);[/glow]
}

void loop() {
}


If you really want to read the values back one at a time, you don't have to create a large scratch buffer, you can use your original trick of reading each element individually (the top code box here).  I just showed the "read it all at once" method to complete the example.

Huzzah, this is my 500th post.

halley

kg4wsv, mikalhart, a preliminary writeup is at http://www.arduino.cc/playground/Code/EEPROMWriteAnything and I'll submit a new version of EEPROM library with useful functions added as static methods.

You can also find this new page via a link from the existing Playground page, http://www.arduino.cc/playground/Code/EEPROM-Flash

Go Up