Data written to struct and pulled through EEPROM gives wrong values

Hello! I am having some issues with pulling my data from the EEPROM correctly. I used a lot of existing forum posts to format the script I currently have but am still having some issues. I have a pump connected to a flow meter and am writing to the EEPROM the value of A0 and using that value to calculate voltage and flow rate (theFlow). The data prints fine through serial.print so something is happening between writing to the struct and pulling it through EEPROM. I have also tried pulling one variable off the eeprom and ot similar data, so I thin my issues lies in how I pull data rather than how I am writing it. Flow should be between 0-4095, voltage should be between 0-3.3V, and theFlow should be between 0-750 ccm (but probably around 200ccm). I am not sure if there is something wrong with the order I have the script it or if I am missing something pertinent to getting the correct output. In addition, it is difficult to tell if I am assigning the values made in the loop to the struct given the above issues, but am I assigning those values correctly?

Here is my code:

#include "Particle.h";
void clearEEPROM() {
	for(int addr = 0; addr < 256; addr++) {
		EEPROM.write(addr, 0);
	}
}

SYSTEM_MODE(SEMI_AUTOMATIC);

size_t eeprom_size = EEPROM.length();
int pump = D0;
float flow = A0;

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

void setup(){
    Serial.begin(9600);
    pinMode(pump, OUTPUT);
    pinMode(flow, OUTPUT);
    digitalWrite(pump, LOW);
    delay(5000);
}

void loop() {
    float flow = (float)analogRead(A0);
    double voltage = (3.3*flow)/4095.0;
        //below is order of 4
    double theFlow = ((750*((voltage/3.3)-0.5))/0.4);
        if (millis() <= 155000) {
        digitalWrite(pump, HIGH);
        }
        else {
            digitalWrite(pump, LOW);       
        }
        for (int i = 0; i < eeprom_size; i++) {
            myObject myObject;
            EEPROM.get(i, myObject);
            Serial.printlnf("i=%f, flow=%f voltage=%f theFlow=%f, sizeof(myObject)=%f",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(" ");
        }    
}

Current data output:

i=0.000000, flow=23798114539864662754282872190323424436536735918671977172300818666169887935376021120766577815793566582932747423712689577171363140584772072552527651183525888.000000 voltage=-0.000000 theFlow=0.000000, sizeof(myObject)=1.745495
 
i=0.000000, flow=0.000000 voltage=0.000000 theFlow=0.000000, sizeof(myObject)=1.745495
 
i=0.000000, flow=23798115526964450517639850316289754158628405690900644617569708015680353919613529880060723516329585171216242998759146677823189386347834751461545304750292992.000000 voltage=0.000000 theFlow=0.000000, sizeof(myObject)=1.745495
 
i=-158456325028528675187087900672.000000, flow=1.587445 voltage=0.000000 theFlow=0.000000, sizeof(myObject)=1.745495
 
i=0.000000, flow=23798114539864763976750972493721429897567517633735449397811273424832234976827119813316397640041960681603234025281883831203172115637775808315288858574979072.000000 voltage=0.000000 theFlow=0.000000, sizeof(myObject)=1.745495
 
i=0.000000, flow=0.000000 voltage=0.000000 theFlow=0.000000, sizeof(myObject)=1.745495
 
i=0.000000, flow=0.000000 voltage=0.000000 theFlow=0.000000, sizeof(myObject)=1.745495

Any help would be greatly appreciated! Thank you!

I think you have several problems in your code. Some of your format specifiers in the printlnf statement are incorrect; the ones for i and sizeof(myObject) should be %d not %f (since those are both integers).

In your for-loop, you are incrementing i in two different places (i++ and i += sizeof(myObject)). In the for-loop statement, you should replace i++ with i += sizeof(myObject), and take that line out of the body of the loop. You should also change the stop parameter for the loop to i < (eeprom_size - sizeof(myObject)) otherwise the last time you try to write to the EEPROM, you might go past the end. However, I don’t understand what you’re trying to do with that for-loop. You’re only reading the pin once each time through loop(), but your for-loop is filling up the EEPROM with the same values over and over (flow, voltage, and theFlow only each have one value as you go through the for-loop).

You’re also storing values that you don’t really need to. Only flow is an independent value; voltage and theFlow are both derived from it, so why store them?

3 Likes

I second @Ric’s assessment and might add two points

  • your code suggests you don’t care too much about flash wear when always filling up the entire 4K block, which will result in many page erase cycles over time, but these are what causes flash wear
  • your clearEEPROM() function would also cause potentially unnecessary erase cycles. If you want to invalidate an EEPROM block, it might be better to just have a single flag that indicates validity and if you want the block to be invalidated, just clear that flag.
    BTW, “cleared” or rather erased EEPROM bytes have the value 0xFF since an EEPROM write can only clear bits but never set them.
2 Likes

Thank you so much for your input. As you can tell, I am very new to this language and microcontrollers. With the for-loop, I am trying to read the previous values stored to the EEPROM and put the next value created from the loop into the EEPROM. How I do I keep from printing the same values to the eeprom every time I go through the loop?

Eventually, I will have multiple sensor values stored on the eeprom that will be pulled at the end of sampling due to lack of wifi during sampling. So this is just to get a working code for when I have those sensors.

Thank you for your input. This is a little above me, can you please clarify or direct me to some literature to clarify your suggestion? Thanks

What exactly is your intent?
Neither of us can help you without exactly knowing what you want.

Currently your code wouldn’t call for EEPROM at all, just use a global variable in RAM and do your stuff.

If you only want to prevent data from being lost due to unexpected resets while the power will still be connected, you may want to use retained variables instead of EEPROM as these are stored in battery backed RAM which does not suffer from flash wear.

Flash wear means that you only have a limited number (~100k to ~1M) of write cycles per cell before single bits start failing beyond repair.
Hence you should not keep writing to flash in an uncontrolled manner.
This might not be an issue if your device is only running a couple of years, but might lead to unexpected failure some time in future.

1 Like

You don't need a for-loop at all, since you only want to store one value each time through loop(). You just need a global or static int to keep track of the address you're writing to (assuming that you actually need to use EEPROM). The Photon only has 2k bytes of EEPROM, but 3k bytes of backup RAM (which you access by using retained variables as @ScruffR mentioned).

As I mentioned above, you don't need to store flow, voltage, and theFlow, since there is only one independent value involved with those three variables. The most space efficient way to store what you want, would be to just store the analogRead value itself, which could be done with a uint16_t (two bytes). If you were to use a retained variable instead of EEPROM, then you could create an array of type uint16_t, and just increment the index of that array every time through loop() to write to the next location in the array.

int index;
retained uint16_t readings[1500];// or smaller if you don't need to store that many values

void loop() {
    uint16_t flow = (uint16_t)analogRead(A0);
    if (index < 1500) {
        readings[index++] = flow;
        double voltage = (3.3*flow)/4095.0;
        double theFlow = ((750*((voltage/3.3)-0.5))/0.4);
        Serial.printlnf("index=%d, flow=%d voltage=%f theFlow=%f", index, flow, voltage, theFlow);
    }
    delay(2000);
}

At one reading every 2 seconds, you will only be able to store data for 50 minutes. If you need to store for longer than that, you either need to increase the delay between readings, or only store the reading if it differs significantly from the previous one (though you probably need to store a time stamp value with each reading if you do it this way).

3 Likes