Pages: [1]   Go Down
Author Topic: Writing float values to Eeprom  (Read 15935 times)
0 Members and 1 Guest are viewing this topic.
London
Offline Offline
Sr. Member
****
Karma: 1
Posts: 330
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset 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;
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);
}
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset 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 Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset 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 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;
}
Logged

Connecticut, US
Offline Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset 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++)
        *p++ = EEPROM.read(ee++);
    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);
    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()
{
}
« Last Edit: February 12, 2009, 06:36:07 pm by halley » Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 5
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Way to go template man.  Woo hoo! indeed.

Mikal
Logged

0
Offline Offline
Faraday Member
**
Karma: 8
Posts: 2526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nice!

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

-j

Logged

London
Offline Offline
Sr. Member
****
Karma: 1
Posts: 330
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset 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.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

Logged

Connecticut, US
Offline Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset 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 Offline
Sr. Member
****
Karma: 1
Posts: 330
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


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

Logged

London
Offline Offline
Sr. Member
****
Karma: 1
Posts: 330
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset 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 Offline
Sr. Member
****
Karma: 1
Posts: 330
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset 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++)
        *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() {
}
Logged

Connecticut, US
Offline Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset 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]
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:
// ... 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.
« Last Edit: February 13, 2009, 01:12:15 pm by halley » Logged

Connecticut, US
Offline Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset 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
Jump to: