Storing data using a struct in EEPROM

We are struggling to retrieve the data from EEPROM using a struct. I have attached our code below. We are trying to store flow, voltage, theFlow, which are double values into the EEPROM. However, we are outputting really weird numbers (I have also attached the output below). We should be getting numbers between 0-4095 for flow and 0-3.3 for voltage. Additionally, we are pulling the data one by one (e.g. callStruct.flow), but is there a way to pull the data at the struct all at once?

I would appreciate any help. Thank you in advance!

#include "Particle.h";

void clearEEPROM();
SYSTEM_MODE(SEMI_AUTOMATIC);

int eeprom_size = 1024;
int pump = D0;
int memoryFull = D7;
int flow = 4;

typedef struct {
    double flow;
    double voltage;
    double theFlow;
    } myObject;

void setup(){
    Particle.variable("pump", pump);
    Time.zone(-5);
    pinMode(pump, OUTPUT);
    digitalWrite(pump, LOW);
    pinMode(memoryFull, OUTPUT);
    Serial.begin(9600);
    delay(5000);
    Serial.println("Sampler 1    Clever-turkey   Flow Meter 1");
    Serial.println("Date   Voltage(V)   Flow(L/min)");
}

void loop() {
        if (millis() <= 1550000) {
        digitalWrite(pump, HIGH);
        }
        else {
            digitalWrite(pump, LOW);       
        }
        for (int i = 0; i < eeprom_size;) {
            myObject callStruct;
            EEPROM.get(i, callStruct);
            Serial.print("memory# ");
            Serial.print(i);
            Serial.print("; things are: ");
            Serial.printlnf("i=%d flow=%d voltage=%d theFlow=%d sizeof(callStruct)=%d", i, callStruct.flow, callStruct.voltage, callStruct.theFlow, sizeof(callStruct));
            double flow = analogRead(A0);
            double voltage = flow/(4095/3.3);
            double theFlow = -3.19*(voltage*voltage*voltage*voltage)+11.52*(voltage*voltage*voltage)-14.81*(voltage*voltage)+8.579*(voltage)-1.762;
            EEPROM.put(i, callStruct);
            i += sizeof(callStruct);
            delay(2000);
            Serial.println(" ");
        }    
}

Output:

Sampler 1    Clever-turkey   Flow Meter 1
Date   Voltage(V)   Flow(L/min)
memory# 0; things are: i=0 flow=1128481603 voltage=17304070 theFlow=-1073741824 sizeof(callStruct)=1159725061
 
memory# 24; things are: i=24 flow=520093696 voltage=0 theFlow=0 sizeof(callStruct)=0
 
memory# 48; things are: i=48 flow=0 voltage=0 theFlow=0 sizeof(callStruct)=-1744568320
 
memory# 72; things are: i=72 flow=64 voltage=-335544320 theFlow=1083654293 sizeof(callStruct)=-2068532060

memory# 96; things are: i=96 flow=605299876 voltage=-1273727996 theFlow=78910632 sizeof(callStruct)=-185276364
 
memory# 120; things are: i=120 flow=881083432 voltage=-1540872120 theFlow=78964788 sizeof(callStruct)=-469441304
 
memory# 144; things are: i=144 flow=-386619212 voltage=-190315276 theFlow=-2072713148 sizeof(callStruct)=-188422092
 
memory# 168; things are: i=168 flow=-1274795836 voltage=-1276599260 theFlow=-305764073 sizeof(callStruct)=-1111736354
 
memory# 192; things are: i=192 flow=-909588538 voltage=-842216502 theFlow=-774844466 sizeof(callStruct)=-707472430
 
memory# 216; things are: i=216 flow=-505356322 voltage=-437984286 theFlow=-370612250 sizeof(callStruct)=-303240214

It looks like you are assigning the values to new variables but never assigning them into your struct object. And so the values are never written to EEPROM.

2 Likes

Just one note on your maths

For maximum precision and performance and also to avoid DIV/0 exceptions (not applicable in this particular case tho’) divisions should be kept to a minimum and get performed as last step when possible.
Consequently I’d rewrite the above this way

double voltage = (3.3 * flow) / 4095.0;

If you mean by “pulling the data” using the individual fields to feed into Serial.printlnf(), then “no” there isn’t since printf() has no way of knowing your structure due to its implementation using dynamic parameter lists (...) where one “anonymous” pointer is passed per parameter and the respective “typecast” is inferred by use of the respective format string field - and there are no format placeholders for non-trivial datatypes.

However, you can always use pointers to access individual struct fields by.

1 Like

@marytovillo, I would add the following “best practice”. Unless you arbitrarily want to limit your use of EEPROM to 1024 bytes, I would rewrite your eeprom_size variable declaration to the following:

size_t eeprom_size = EEPROM.length();

The standard EEPROM size for a Photon and Electron is 2047 bytes.

I also wouldn’t create a new callStruct (myObject) variable in each for loop. One global declaration would suffice.

2 Likes

Hi! Thank you for your input. We moved things around and made the changes you suggested, but we are still getting weird numbers and flow is now outputting 0 each time. Any suggestions? I have attached the changes we made to the code. Thank you very much!

void loop() {
        if (millis() <= 1550000) {
        digitalWrite(pump, HIGH);
        }
        else {
            digitalWrite(pump, LOW);       
        }
        double flow = analogRead(A0);
        double voltage = (3.3*flow)/4095.0;
        //below is order of 4
        double theFlow = -3.19*(voltage*voltage*voltage*voltage)+11.52*(voltage*voltage*voltage)-14.81*(voltage*voltage)+8.579*(voltage)-1.762;
        for (int i = 0; i < eeprom_size;) {
            myObject myObject;
            EEPROM.get(i, myObject);
            Serial.print("memory# ");
            Serial.print(i);
            Serial.print("; things are: ");
            Serial.printlnf("i=%d, flow=%d voltage=%d theFlow=%d, sizeof(myObject)=%d", i, myObject.flow, myObject.voltage, myObject.theFlow, sizeof(myObject));
            myObject.flow += flow;
            myObject.voltage += voltage;
            myObject.theFlow += theFlow;
            EEPROM.put(i, myObject);
            i += sizeof(myObject);
            delay(2000);
            Serial.println(" ");
        }    
}

@marytovillo, couple things:

analogRead() returns a type int so you may want to cast it as double with:
double flow = (double)analogRead(A0);

The %d specifies a signed decimal integer. You should be using %f to print floats and doubles. Take a look at these docs for using printf and how specify the number of decimals that are printed:

http://www.cplusplus.com/reference/cstdio/printf/

3 Likes