Pages: [1]   Go Down
 Author Topic: Writing float values to Eeprom  (Read 14635 times) 0 Members and 1 Guest are viewing this topic.
London
Offline
Sr. Member
Karma: 1
Posts: 330
Arduino rocks
 « on: February 12, 2009, 05:21:30 pm » Bigger Smaller Reset

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:
#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;

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

// write float array to eeprom
// float value takes 4 bytes
for (int i=0; i < noElem-1; i++){
}

for (int i=0; i < noElem-1; i++){
Serial.print((long)val);
}
}

void loop() {

}

{
byte Byte1 = ((p_value >> 0) & 0xFF);
byte Byte2 = ((p_value >> 8) & 0xFF);
byte Byte3 = ((p_value >> 16) & 0xFF);
byte Byte4 = ((p_value >> 24) & 0xFF);

}

{

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);
}
 Logged

London
Offline
Tesla Member
Karma: 10
Posts: 6252
Have fun!
 « Reply #1 on: February 12, 2009, 05:57:42 pm » Bigger Smaller Reset

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;
}
 Logged

Connecticut, US
Offline
Edison Member
Karma: 2
Posts: 1036
Whatduino
 « Reply #2 on: February 12, 2009, 06:05:22 pm » Bigger Smaller Reset

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:
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 value = 0.0;
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++)
return value;
}
 Logged

Connecticut, US
Offline
Edison Member
Karma: 2
Posts: 1036
Whatduino
 « Reply #3 on: February 12, 2009, 06:32:21 pm » Bigger Smaller Reset

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:
#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++)
return i;
}

For testing and demonstrations:

Code:
// 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);
Serial.println(n);
Serial.println(by, HEX);

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

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

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

double pi = 3.1415926538;

struct st_t st;
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);
Serial.println(n);
Serial.println(st.lo);
Serial.println(st.by, HEX);
Serial.println(st.db == pi);
}

void loop()
{
}
 « Last Edit: February 12, 2009, 06:36:07 pm by halley » Logged

Austin, TX USA
Offline
God Member
Karma: 4
Posts: 997
Arduino rocks
 « Reply #4 on: February 13, 2009, 01:27:50 am » Bigger Smaller Reset

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

Way to go template man.  Woo hoo! indeed.

Mikal
 Logged

0
Offline
Karma: 8
Posts: 2526
 « Reply #5 on: February 13, 2009, 07:09:24 am » Bigger Smaller Reset

Nice!

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

-j

 Logged

London
Offline
Sr. Member
Karma: 1
Posts: 330
Arduino rocks
 « Reply #6 on: February 13, 2009, 07:36:44 am » Bigger Smaller Reset

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.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

 Logged

Connecticut, US
Offline
Edison Member
Karma: 2
Posts: 1036
Whatduino
 « Reply #7 on: February 13, 2009, 07:56:16 am » Bigger Smaller Reset

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.
 « Last Edit: February 13, 2009, 07:58:12 am by halley » Logged

London
Offline
Sr. Member
Karma: 1
Posts: 330
Arduino rocks
 « Reply #8 on: February 13, 2009, 08:55:32 am » Bigger Smaller Reset

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

 Logged

London
Offline
Sr. Member
Karma: 1
Posts: 330
Arduino rocks
 « Reply #9 on: February 13, 2009, 11:52:21 am » Bigger Smaller Reset

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
 Logged

London
Offline
Sr. Member
Karma: 1
Posts: 330
Arduino rocks
 « Reply #10 on: February 13, 2009, 12:44:15 pm » Bigger Smaller Reset

Works great and is easy to use.
Thanks Halley.

Code:
#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++)
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 n = 0;

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

// write data to eeprom
for (int i=0; i <= noElem-1; i++){
}

for (int i=0; i <= noElem-1; i++){
double val;
Serial.println(val);
}
}

void loop() {
}
 Logged

Connecticut, US
Offline
Edison Member
Karma: 2
Posts: 1036
Whatduino
 « Reply #11 on: February 13, 2009, 01:08:01 pm » Bigger Smaller Reset

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:
// ... 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]
[glow]// unsigned int n = 0; // use a local variable for this[/glow]

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

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

for (int i=0; i < [glow]countof(testInt)[/glow]; i++){
double val;
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:
// ... 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};

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

// write data to eeprom all at once

// read data back in separate temporary buffer
[glow]double scratch[countof(testInt)];
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.
 « Last Edit: February 13, 2009, 01:12:15 pm by halley » Logged

Connecticut, US
Offline
Edison Member
Karma: 2
Posts: 1036
Whatduino
 « Reply #12 on: February 13, 2009, 01:44:00 pm » Bigger Smaller Reset

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
 Logged

 Pages: [1]   Go Up