How do you get a weight value stored in the EEPROM

I am working on a sketch for a load cell scale that uses four 50kg load cells and an HX711 amplifier. I have the scale completed and I’m now trying to get the code to perform all the necessary functions. I posted a question a few days ago asking for help with the sketch because I am an absolute beginner and couldn’t get the sketch to work for me. Luckily, I was helped by StefanL38 and he added some debugging code and was able to get the code to load up properly. I have been working with the code myself now but have run into some other problems. The first thing I did was change the weight from displaying in KGS to displaying in LBS and that seemed to go ok for me. Next, I wanted to add a tare function so that I could call it from the serial monitor. My display was always reading very high with no weigh on the scale. It was reading 877.50 lbs. I was able to enter this into the code and it seemed to work because I can now tare the scale to zero using the serial monitor. The scale seems fairly accurate. But when I shutdown the power to the board and power it back up the scale display reads 877.50 lbs. again. I want to have a scale that the weight can be left on it all the time. I want to power up the scale once per day and read the weight to see if it has accumulated more weight. So I need to be able to have the weight value stored in the EEPROM at shutdown so that it can retrieve that weight at the next power up. I thought the sketch that I have was capable of doing that already. I was wondering if I put the new tare code in the wrong place (because I basically know very little about this)) and this is causing the issue. Does anyone have any ideas for me?

#define CodeVersion "Code-Version 010"

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
#define dbg(myFixedText, variableName) \
  Serial.print(F(#myFixedText " " #variableName "=")); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName, timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if (millis() - intervalStartTime >= timeInterval) { \
      intervalStartTime = millis(); \
      Serial.print(F(#myFixedText " " #variableName "=")); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if (lastState != variableName) { \
      Serial.print(F(#myFixedText " " #variableName " changed from ")); \
      Serial.print(lastState); \
      Serial.print(F(" to ")); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if (lastState != variableName) { \
      Serial.print(F(#myFixedText " " #variableName " changed from ")); \
      Serial.print(lastState); \
      Serial.print(F(" to ")); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

#include <HX711_ADC.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <U8g2lib.h>

#if defined(ESP8266) || defined(ESP32) || defined(AVR)
#include <EEPROM.h>
#endif

#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 32  // OLED display height, in pixels
#define Y_POS_0 0
#define X_POS_0 0
#define Y_POS_10 10
#define X_POS_10 10
#define TEXT_SIZE_SMALL 1
#define TEXT_SIZE_MED 2
#define TEXT_SIZE_LARGE 3
#define LONG_DELAY 4000
#define MED_DELAY 2000
#define SHORT_DELAY 1000

#define OLED_RESET -1        // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C  ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
//Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//pins:
//OLED USES I2C -> ON ARDUINO UNO: SCL @ A5, SDA @ A4
const int HX711_dout = 4;  //mcu > HX711 dout pin
const int HX711_sck = 5;   //mcu > HX711 sck pin

//HX711 constructor:
HX711_ADC LoadCell(HX711_dout, HX711_sck);

const int calVal_eepromAdress = 0;
const int tareOffsetVal_eepromAdress = 4;
unsigned long t = 0;

void PrintFileNameDateTime() {
  Serial.println(CodeVersion);
  Serial.println(F("Code running comes from file "));
  Serial.println(F(__FILE__));
  Serial.print(F("  compiled "));
  Serial.print(F(__DATE__));
  Serial.print(F(" "));
  Serial.println(F(__TIME__));
}

void HX711_setup() {
  //SETUP LOAD CELLS
  LoadCell.begin();
  Serial.println(F("LoadCell.begin() done"));
  //LoadCell.setReverseOutput();
  float calibrationValue;
  //20.9  // calibration value (see example file "Calibration.ino")

#if defined(ESP8266) || defined(ESP32)
  EEPROM.begin(512);
#endif

  EEPROM.get(calVal_eepromAdress, calibrationValue);  // uncomment this if you want to fetch the calibration value from eeprom
  Serial.println(F("EEPROM.get(calVal_eepromAdress done"));
  dbg("1:", calVal_eepromAdress);
  dbg("2:", calibrationValue);
  if (calibrationValue == 0.0) {
    calibrationValue = 1.0;
    Serial.println(F("calibrationValue == 0.0 set to 1.0"));
  }

  dbg("nan", isnan(calibrationValue));
  if ((isnan(calibrationValue) == 1)) {
    Serial.println(F("calibrationValue nan set to 2.0"));
    calibrationValue = 2.0;
  }
  dbg(F("finally"), calibrationValue);

  //restore the zero offset value from eeprom:
  long tare_offset = 0;
  EEPROM.get(tareOffsetVal_eepromAdress, tare_offset);
  Serial.println(F("EEPROM.get(tareOffsetVal_eepromAdress, tare_offset) done"));
  dbg("3:", tareOffsetVal_eepromAdress);
  dbg("4:", tare_offset);

  if (tare_offset == 0) {
    tare_offset = 10;
  }
  dbg("finally", tare_offset);


  LoadCell.setTareOffset(tare_offset);
  Serial.println(F("LoadCell.setTareOffset(tare_offset) done"));

  unsigned long stabilizingtime = 2000;  // preciscion right after power-up can be improved by adding a few seconds of stabilizing time
  LoadCell.start(stabilizingtime, false);
  if (LoadCell.getTareTimeoutFlag()) {
    Serial.println(F("Timeout, check MCU>HX711 wiring and pin designations"));
    while (1)
      ;
  } else {
    LoadCell.setCalFactor(calibrationValue);  // set calibration value (float)
    Serial.println(calibrationValue);
    Serial.println(F("HX771-Startup is complete"));
  }
}


void OLED_Setup() {
  //SETUP OLED
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }
  /*
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;  // Don't proceed, loop forever
  }
  */
  Serial.println(F("if (!display.begin( done"));

  // Clear the buffer
  display.clearDisplay();
  Serial.println(F("display.clearDisplay() done"));

  // Draw a single pixel in white
  Serial.println(F("start printing to display"));
  display.clearDisplay();
  display.setTextSize(TEXT_SIZE_SMALL);
  display.setTextColor(SSD1306_WHITE);     // Draw white text
  display.setCursor(Y_POS_0, X_POS_0);     // Start at top-left corner
  display.println(F("watermelons 2023"));  //customize your own start screen message
  display.println(F("\nInitializing"));
  display.display();
  Serial.println(F("printing to display done"));
  delay(SHORT_DELAY);
}


void setup() {
  Serial.begin(115200);
  Serial.println(F("Serial.begin(115200) done delay(2000)"));
  delay(2000);
  Serial.println();
  Serial.println(F("Starting..."));
  PrintFileNameDateTime();

  OLED_Setup();
  HX711_setup();

  Serial.println(F("exiting setup()"));
}


void setDisplayParameters(int cursorPosY, int cursorPosX, int textSize) {
  //Serial.println( F("entering setDisplayParameters()") );

  display.clearDisplay();
  display.setTextSize(textSize);
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.setCursor(cursorPosY, cursorPosX);  // Starting point of display
  //Serial.println( F("exiting setDisplayParameters()") );
}


void displayWeightInLBs(float weightInGrams, int cursorPosY, int cursorPosX, int size, int delayTime) {
  //Serial.println( F("entering displayWeightInKGs()") );
  setDisplayParameters(cursorPosY, cursorPosX, size);
  float weight = weightInGrams / 453.592;
  if (weight < 0) {
    weight = 0.00;
  }

  String weightInKGs = String(weight);
  display.println(weightInKGs);
  display.display();
  delay(delayTime);
  //Serial.println(F("exiting displayWeightInKGs()") );
}


void loop() {
  //GET VALUES FROM LOAD CELLS
  float value = 0.0;
  static boolean newDataReady = 0;
  const int serialPrintInterval = 2000;  //increase value to slow down serial print activity

  // Check for user input from the serial monitor
  if (Serial.available() > 0) {
    char input = Serial.read();
    if (input == 't') {
      LoadCell.tare();
      Serial.println("Tare complete");
    }
  }
  // check for new data/start next conversion:
  uint8_t updateResult;
  updateResult = LoadCell.update();
  if (updateResult) {
    dbgi(F("LCU"), updateResult, 1000);
    newDataReady = true;
  } else {
    dbgi(F("LCU"), updateResult, 1000);
  }

  // get smoothed value from the dataset:
  if (newDataReady) {
    if (millis() - t >= serialPrintInterval) {
      float value = LoadCell.getData();
      //Serial.print( F("Load_cell output val: ") );
      Serial.println(value);
      //DISPLAY VALUE ON THE OLED
      displayWeightInLBs(value, Y_POS_10, X_POS_10, TEXT_SIZE_LARGE, SHORT_DELAY);
      newDataReady = false;
      t = millis();
    }
  }
}

I just realized that I put this in the wrong category. Sorry!

Moved into the Programming Questions section of the forum.

How do you want it to work with/against the Tare functionality?

If you do the tare function while the scale is loaded, it will zero out the weight.

If you then want to reset the zeroed tare value so that getData() returns a specific, saved weight, you'd need to compute the corresponding tare value from that weight, the current getCalFactor() and the current getTareValue().

LoadCell.setTareOffset( LoadCell.getTareOffset() - weight * LoadCell.getCalFactor());

Maybe you want some functionality to save and restore tare values in EEPROM? Or maybe read and write and save tare values through the serial monitor?

What I would like the to scale to do is save the last know weight that the scale was reading to be saved and then recalled the next time I power it back on. For instance, If the scale had 100 lbs sitting on it and I power it off with that weight still on the scale. After a period of time, lets say 3 more lbs was added to the powered off scale. I would like to power that scale back up to see how much more weigh has been added since the power off. It should read 103lbs. That is what I am looking for. Hope that make sense. Thanks!

That makes sense. I think the issue is that the tare value that is saved in the EEPROM isn't correct.

I'd add another function to store the calibration and tare settings using EEPROM.put() to match

and

and call it with an additional command in this bit:

Something like this completely untested code:

if (input == 'w') {
  EEPROM.put(calVal_eepromAdress, calibrationValue);       
  EEPROM.put(tareOffsetVal_eepromAdress, tare_offset);
  Serial.println("Tare and calibration written to EEPROM");
}

Then the configuration values that are in use at the time of writing should be written into EEPROM, so that the setup code that reads them should get the proper values to use.

Thanks Dave for trying to help me and I'm really sorry to have to ask you this. I understand that you want me to put the above codes into the sketch to test it out. I am so new at this I don't know for sure where I'm supposed to put it. I'm assuming the two "if" statements would go in the loop function. Is that correct? I'm not sure were the other two lines go.

Here's some partially tested code, modifying your t command to print out both configuration numbers, and adding a w command to write those values to EEPROM.

#define CodeVersion "Code-Version 010"

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
#define dbg(myFixedText, variableName) \
  Serial.print(F(#myFixedText " " #variableName "=")); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName, timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if (millis() - intervalStartTime >= timeInterval) { \
      intervalStartTime = millis(); \
      Serial.print(F(#myFixedText " " #variableName "=")); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if (lastState != variableName) { \
      Serial.print(F(#myFixedText " " #variableName " changed from ")); \
      Serial.print(lastState); \
      Serial.print(F(" to ")); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if (lastState != variableName) { \
      Serial.print(F(#myFixedText " " #variableName " changed from ")); \
      Serial.print(lastState); \
      Serial.print(F(" to ")); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

#include <HX711_ADC.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <U8g2lib.h>

#if defined(ESP8266) || defined(ESP32) || defined(AVR)
#include <EEPROM.h>
#endif

#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 32  // OLED display height, in pixels
#define Y_POS_0 0
#define X_POS_0 0
#define Y_POS_10 10
#define X_POS_10 10
#define TEXT_SIZE_SMALL 1
#define TEXT_SIZE_MED 2
#define TEXT_SIZE_LARGE 3
#define LONG_DELAY 4000
#define MED_DELAY 2000
#define SHORT_DELAY 1000

#define OLED_RESET -1        // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C  ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
//Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//pins:
//OLED USES I2C -> ON ARDUINO UNO: SCL @ A5, SDA @ A4
const int HX711_dout = 4;  //mcu > HX711 dout pin
const int HX711_sck = 5;   //mcu > HX711 sck pin

//HX711 constructor:
HX711_ADC LoadCell(HX711_dout, HX711_sck);

const int calVal_eepromAdress = 0;
const int tareOffsetVal_eepromAdress = 4;
unsigned long t = 0;

void PrintFileNameDateTime() {
  Serial.println(CodeVersion);
  Serial.println(F("Code running comes from file "));
  Serial.println(F(__FILE__));
  Serial.print(F("  compiled "));
  Serial.print(F(__DATE__));
  Serial.print(F(" "));
  Serial.println(F(__TIME__));
}

void HX711_setup() {
  //SETUP LOAD CELLS
  LoadCell.begin();
  Serial.println(F("LoadCell.begin() done"));
  //LoadCell.setReverseOutput();
  float calibrationValue;
  //20.9  // calibration value (see example file "Calibration.ino")

#if defined(ESP8266) || defined(ESP32)
  EEPROM.begin(512);
#endif

  EEPROM.get(calVal_eepromAdress, calibrationValue);  // uncomment this if you want to fetch the calibration value from eeprom
  Serial.println(F("EEPROM.get(calVal_eepromAdress done"));
  dbg("1:", calVal_eepromAdress);
  dbg("2:", calibrationValue);
  if (calibrationValue == 0.0) {
    calibrationValue = 1.0;
    Serial.println(F("calibrationValue == 0.0 set to 1.0"));
  }

  dbg("nan", isnan(calibrationValue));
  if ((isnan(calibrationValue) == 1)) {
    Serial.println(F("calibrationValue nan set to 2.0"));
    calibrationValue = 2.0;
  }
  dbg(F("finally"), calibrationValue);

  //restore the zero offset value from eeprom:
  long tare_offset = 0;
  EEPROM.get(tareOffsetVal_eepromAdress, tare_offset);
  Serial.println(F("EEPROM.get(tareOffsetVal_eepromAdress, tare_offset) done"));
  dbg("3:", tareOffsetVal_eepromAdress);
  dbg("4:", tare_offset);

  if (tare_offset == 0) {
    tare_offset = 10;
  }
  dbg("finally", tare_offset);


  LoadCell.setTareOffset(tare_offset);
  Serial.println(F("LoadCell.setTareOffset(tare_offset) done"));

  unsigned long stabilizingtime = 2000;  // preciscion right after power-up can be improved by adding a few seconds of stabilizing time
  LoadCell.start(stabilizingtime, false);
  if (LoadCell.getTareTimeoutFlag()) {
    Serial.println(F("Timeout, check MCU>HX711 wiring and pin designations"));
    while (1)
      ;
  } else {
    LoadCell.setCalFactor(calibrationValue);  // set calibration value (float)
    Serial.println(calibrationValue);
    Serial.println(F("HX771-Startup is complete"));
  }
}


void OLED_Setup() {
  //SETUP OLED
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }
  /*
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;  // Don't proceed, loop forever
  }
  */
  Serial.println(F("if (!display.begin( done"));

  // Clear the buffer
  display.clearDisplay();
  Serial.println(F("display.clearDisplay() done"));

  // Draw a single pixel in white
  Serial.println(F("start printing to display"));
  display.clearDisplay();
  display.setTextSize(TEXT_SIZE_SMALL);
  display.setTextColor(SSD1306_WHITE);     // Draw white text
  display.setCursor(Y_POS_0, X_POS_0);     // Start at top-left corner
  display.println(F("watermelons 2023"));  //customize your own start screen message
  display.println(F("\nInitializing"));
  display.display();
  Serial.println(F("printing to display done"));
  delay(SHORT_DELAY);
}


void setup() {
  Serial.begin(115200);
  Serial.println(F("Serial.begin(115200) done delay(2000)"));
  delay(2000);
  Serial.println();
  Serial.println(F("Starting..."));
  PrintFileNameDateTime();

  OLED_Setup();
  HX711_setup();

  Serial.println(F("exiting setup()"));
}


void setDisplayParameters(int cursorPosY, int cursorPosX, int textSize) {
  //Serial.println( F("entering setDisplayParameters()") );

  display.clearDisplay();
  display.setTextSize(textSize);
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.setCursor(cursorPosY, cursorPosX);  // Starting point of display
  //Serial.println( F("exiting setDisplayParameters()") );
}


void displayWeightInLBs(float weightInGrams, int cursorPosY, int cursorPosX, int size, int delayTime) {
  //Serial.println( F("entering displayWeightInKGs()") );
  setDisplayParameters(cursorPosY, cursorPosX, size);
  float weight = weightInGrams / 453.592;
  if (weight < 0) {
    weight = 0.00;
  }

  String weightInKGs = String(weight);
  display.println(weightInKGs);
  display.display();
  //delay(delayTime);
  //Serial.println(F("exiting displayWeightInKGs()") );
}


void loop() {
  //GET VALUES FROM LOAD CELLS
  float value = 0.0;
  static boolean newDataReady = 0;
  const int serialPrintInterval = 2000;  //increase value to slow down serial print activity

  // Check for user input from the serial monitor
  if (Serial.available() > 0) {
    char input = Serial.read();
    if (input == 't') {
      LoadCell.tare();
      Serial.print("Tare complete with:");
      Serial.print(LoadCell.getTareOffset());
      Serial.print(" Calibration:");
      Serial.println(LoadCell.getCalFactor());
    }
    if (input == 'w') {
      long tare_offset = LoadCell.getTareOffset();
      EEPROM.put(calVal_eepromAdress, LoadCell.getCalFactor());       
      EEPROM.put(tareOffsetVal_eepromAdress, LoadCell.getTareOffset());
      Serial.println("Tare and calibration written to EEPROM");
    }
  }
  // check for new data/start next conversion:
  uint8_t updateResult;
  updateResult = LoadCell.update();
  if (updateResult) {
    dbgi(F("LCU"), updateResult, 1000);
    newDataReady = true;
  } else {
    dbgi(F("LCU"), updateResult, 1000);
  }

  // get smoothed value from the dataset:
  if (newDataReady) {
    if (millis() - t >= serialPrintInterval) {
      float value = LoadCell.getData();
      //Serial.print( F("Load_cell output val: ") );
      Serial.println(value);
      //DISPLAY VALUE ON THE OLED
      displayWeightInLBs(value, Y_POS_10, X_POS_10, TEXT_SIZE_LARGE, SHORT_DELAY);
      newDataReady = false;
      t = millis();
    }
  }
}

This mostly compiles in Wokwi, but I don't have your hardware or wiring, and didn't include the U8g2lib.h

I made some changes to my suggestion to read the values directly from the LoadCell object, and when 't'aring, I print out the calibration and tare offset.

My hope is that you'd set the scale up empty, send a 't'are command, and then send a 'w'rite command, and then after a power cycle, the existing setup commands would read and use the EEPROM values that were written earlier.

1 Like

Dave, You have no idea how happy this makes me feel. I uploaded the code and entered "t" to tare and then "w" and it seems to work perfectly. If I power it off with the weigh still on the scale it displays the same weigh when I power it back up. That is exactly what I wanted it to do. The only small issue is the scale weighs very slightly heavy (but I could live with that). It shows me 3 lbs. heavier than I am. I was wondering it there was a particular line in the code I could go to play around with entering a slightly different calibration factor. If not, I'm happy with it like it is.
I have only been a member here for a few days and I can't believe how helpful everyone is on here.
Thanks again so much!!!

1 Like

I'm glad it works.

The calibration line might be a bit tricky, since we've now written a default number it into EEPROM, and since that the number read back from EEPROM isn't 0, it doesn't try to update it:

One way to do it would be to re-comment the EEPROM calibration value and hard-code an updated value into that routine. Here's some more untested code, assuming it currently reports 150# when you actually weigh 153#:

///  EEPROM.get(calVal_eepromAdress, calibrationValue);  // uncomment this if you want to fetch the calibration value from eeprom
  Serial.println(F("EEPROM.get(calVal_eepromAdress done"));
  dbg("1:", calVal_eepromAdress);
  dbg("2:", calibrationValue);
  if (calibrationValue == 0.0) {
    calibrationValue = 1.0 *150/153;  // 3# heavier than 150#
    Serial.println(F("calibrationValue == 0.0 set to 1.0"));
  }

  dbg("nan", isnan(calibrationValue));
  if ((isnan(calibrationValue) == 1)) {
    Serial.println(F("calibrationValue nan set to 2.0"));
    calibrationValue = 2.0;
  }

One thing I can't test on my end is if your code triggers the if(calibrationValue ==0) or the if((isnan(calibrationValue)) test, so you might have to change one or the other.

Or you could take the value printed out from the tare command, and adjust it as needed.

The source on the setCalFactor says how it is used:

I appreciate that Info Dave. I may play around with it. But for what I'm going to use if for this will likely be close enough. Thanks again!!!!

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.