Need help with loadcell program and float calculations

Greetings!

I've been working with load cell and Arduino, and I need to perform some calculations on the value read by my load cell. the calculations are as follows:

  1. User needs to pour 500 ml of liquid in the tank fitted on load cell
  2. Then the Arduino is supposed to divide 500 by the output value of load cell.
  3. Then whatever the result is use it to multiply with the freshly read loadcell value again.

I have attached the code I'm using. Problem, is I get output even without placing anything on the loadcell. I have ran calibration code and updated tareoffset value and calibrationfactor accordingly.

Fuel_Checking_System_V1.8.0.ino (9.82 KB)

...
    if (millis() > t + serialPrintInterval) 
...
    if (millis() > t + serialPrintInterval) 
...

That pains me.

If you refactor the code into more of a state machine, you can avoid a lot of those if/else statements. Something like this (and use an enum to make which page you on easier for humans to read)

#include <HX711_ADC.h>
#include <EEPROM.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>

const int RXPin = 2;
const int TXPin = 3;
const int GPSBaud = 9600;

const int HX711_dout = A4;
const int HX711_sck = A5;

HX711_ADC LoadCell(HX711_dout, HX711_sck);
TinyGPSPlus gps;
SoftwareSerial gpsSerial(RXPin, TXPin);

const unsigned long serialPrintInterval = 100;

float calibrationValue = 430.08;
long TareOffset = 8342702;

#include <LiquidCrystal.h>
LiquidCrystal lcd (10, 9, 8, 7, 6, 5);

const byte BtnA = 4;
const byte BtnB =  A1;
const byte BtnC = A2;
const byte BtnD = A3;

enum { pHOME, pSELECT, pFILL, pQUALITY, pFILLING, pCONFIRM };
int page = pHOME;

float weight;
float kgs;
float units_ml;
float mass;
int petrol = 0;
int diesel = 0;

boolean SwA = false;
boolean SwB = false;
boolean SwC = false;
boolean SwD = false;

const unsigned long debounceDelay = 50;

boolean pass = false;

void checkIf_A_ButtonIsPressed()
{
  static unsigned long lastTime;
  static int lastState;

  int state = digitalRead(BtnA);

  if (state != lastState) lastTime = millis();

  if (millis() - lastTime >= debounceDelay) {
    if (state == 0) SwA = true;
  }
  lastState = state;
}


void checkIf_B_ButtonIsPressed()
{
  static unsigned long lastTime;
  static int lastState;

  int state = digitalRead(BtnB);

  if (state != lastState) lastTime = millis();

  if (millis() - lastTime >= debounceDelay) {
    if (state == 0) SwB = true;
  }
  lastState = state;
}

void checkIf_C_ButtonIsPressed()
{
  static unsigned long lastTime;
  static int lastState;

  int state = digitalRead(BtnC);

  if (state != lastState) lastTime = millis();

  if (millis() - lastTime >= debounceDelay) {
    if (state == 0) SwC = true;
  }
  lastState = state;
}

void checkIf_D_ButtonIsPressed()
{
  static unsigned long lastTime;
  static int lastState;

  int state = digitalRead(BtnD);

  if (state != lastState) lastTime = millis();

  if (millis() - lastTime >= debounceDelay) {
    if (state == 0) SwD = true;
  }
  lastState = state;
}

void DisplayFuel()
{
  lcd.setCursor(19, 3);
  if (petrol) lcd.print("P");
  else if (diesel) lcd.print("D");
  else lcd.print("?");
}

void makeDecision()
{
  float minWeight, maxWeight;

  lcd.setCursor(0, 1);
  if (petrol) {
    minWeight = 720.0;
    maxWeight = 775.0;
  }
  else if (diesel) {
    minWeight = 820.0;
    maxWeight = 880.0;
  }
  else {
    lcd.print("???");
    return;
  }

  if (weight > minWeight && weight < maxWeight)  {
    lcd.print("Good.");
  }
  else {
    lcd.print("Bad.");
  }
}

void checkFuelQuality()
{
  chkWeight();
  makeDecision();
}

void getWeight() {
  readWeight(true);
}
void chkWeight() {
  readWeight(false);
}

void readWeight(bool report)
{
  static unsigned long lastSerialTime;

  bool newDataReady = false;

  if (LoadCell.update()) newDataReady = true;

  if (newDataReady) {
    if (millis() - lastSerialTime >= serialPrintInterval) {
      weight = LoadCell.getData();
      lastSerialTime = millis();
    }
    if ( report ) {
      Serial.print("Raw weight:");
      Serial.println(weight);
      Serial.print("Unit/ml factor:");
      Serial.println(units_ml);
      mass = weight * units_ml;
      Serial.print("Mass:");
      Serial.println(mass);
      if (mass >= 1000) {
        kgs = mass / 1000.0;
      }
      Serial.print("Converted in ltrs:");
      Serial.println(kgs);
      lcd.print(kgs);
      lcd.print("Ltrs");
      DisplayFuel();
    }
  }
}

void get_unit_mass()
{
  units_ml = 500.0 / weight;
}

void setup()
{
  Serial.begin(115200);
  LoadCell.begin();
  gpsSerial.begin(GPSBaud);

  LoadCell.start(2000, true);

  if (LoadCell.getTareTimeoutFlag()) {
    Serial.println("Timeout, check MCU>HX711 wiring and pin designations");
    while (1);
  }
  else {
    LoadCell.setCalFactor(calibrationValue);
    LoadCell.setTareOffset(TareOffset);
    Serial.println("Startup is complete");
  }

  pinMode(BtnA, INPUT_PULLUP);
  pinMode(BtnB, INPUT_PULLUP);
  pinMode(BtnC, INPUT_PULLUP);
  pinMode(BtnD, INPUT_PULLUP);

  lcd.begin(20, 4);
  lcd.setCursor(5, 0);
  lcd.print("Welcome to");
  lcd.setCursor(2, 1);
  lcd.print("Ready Automotive");
  lcd.setCursor(0, 3);
  lcd.print("Technology Solutions");

  delay(3000);
  lcd.clear();
}

void loop()
{
  while (gpsSerial.available() > 0) {
    gps.encode(gpsSerial.read());
    if (gps.location.isUpdated()) {
      chkWeight();
      Serial.print("<");
      Serial.print(gps.location.lat(), 6);
      Serial.print(",");
      Serial.print(gps.location.lng(), 6);
      Serial.print(",");
      Serial.print(weight);
      Serial.println(">");
    }
  }
  checkIf_A_ButtonIsPressed();
  checkIf_B_ButtonIsPressed();
  checkIf_C_ButtonIsPressed();
  checkIf_D_ButtonIsPressed();

  drawMenu(page);
}

void drawMenu(int page)
{
  static int currentPage = -1;
  bool redraw =  (page != currentPage );

  if ( redraw ) {
    lcd.clear();
  }

  switch (page) {
    case pHOME:
      if ( redraw ) {
        lcd.setCursor(3, 1);
        lcd.print("Fuel Volume:");
        lcd.setCursor(5, 2);
      }
      getWeight();

      if (SwA) {
        SwA = false;
        if (petrol == 1 || diesel == 1) page = pFILL;
        else page = pCONFIRM;
      }

      if (SwD) {
        SwD = false;
        page = pSELECT;
      }

      break;

    case pSELECT:
      if ( redraw ) {
        lcd.print("Select Fuel");
        lcd.setCursor(0, 1);
        lcd.print("Press:");
        lcd.setCursor(0, 2);
        lcd.print("A: Petrol  B: Diesel");
      }
      if (SwA) {
        SwA = false;
        lcd.setCursor(0, 3);
        lcd.print("Petrol Selected");
        LoadCell.tareNoDelay();
        delay(1000);
        page = pFILL;
        petrol = 1;
        diesel = 0;
      }
      if (SwB) {
        SwB = false;
        lcd.setCursor(0, 3);
        lcd.print("Diesel Selected");
        LoadCell.tareNoDelay();
        delay(1000);
        page = pFILL;
        diesel = 1;
        petrol = 0;
      }
      break;

    case pFILL:
      if (redraw) {
        lcd.print("Please fill");
        lcd.setCursor(0, 1);
        lcd.print("500ml Fuel");
        lcd.setCursor(0, 2);
        lcd.print("Press A");
        lcd.setCursor(0, 3);
        lcd.print("when done");
        DisplayFuel();
      }
      if (SwA) {
        SwA = false;
        chkWeight();
        get_unit_mass();
        page = pQUALITY;
      }
      break;

    case pQUALITY:
      if ( redraw ) {
        lcd.print("Fuel Quality is:");
        checkFuelQuality();
        lcd.setCursor(6, 1);
        lcd.print("Proceed?");
        lcd.setCursor(0, 2);
        lcd.print("A -> Yes");
        lcd.setCursor(12, 2);
        lcd.print("B -> NO");
        DisplayFuel();
      }
      if (SwA) {
        SwA = false;
        page = pFILLING;
        pass = true;
      }
      if (SwB) {
        SwB = false;
        page = pHOME;
      }
      break;

    case pFILLING:
      if ( redraw ) {
        lcd.print("Tank being filled");
        lcd.setCursor(0, 1);
        lcd.print("Please wait...");
        lcd.setCursor(0, 3);
        lcd.print("Press B to exit");
        DisplayFuel();
      }
      lcd.setCursor(0, 2);
      getWeight();
      if (SwB) {
        SwB = false;
        page = pHOME;
      }
      break;

    case pCONFIRM:
      if ( redraw ) {
        lcd.print("Please Select Fuel");
        lcd.setCursor(0, 1);
        lcd.print("Press D");
        lcd.setCursor(0, 2);
        lcd.print("to select Fuel");
      }
      if (SwD) {
        SwD = false;
        page = pSELECT;
      }
      
      break;
  }
}

blh64:
If you refactor the code into more of a state machine, you can avoid a lot of those if/else statements. Something like this (and use an enum to make which page you on easier for humans to read

That was jus AMAZING!
It reduced the code length as well and made it more readable. My apologies for the messy code up there, I'm still trying to acquire the skills for state machine.
I would like to ask how the value of page variable changes? and how the function readWeight() works. My code requires reading the weight for one instance and then do some calculations on freshly read load cell values. I'm not able to understand how the same function will do both of these tasks.
Secondly, will it fix the problem which I'm encountering? or does it still need to be addressed? I guess I'm getting faulty results becoz of the overloading of variable types. I read about floats, longs and doubles but I wasn't able to relate it with my code.
What should be my further step? Thanks in advance and have a nice dayy! :slight_smile:

The variable page is an ordinary 'int' so just assign to it. You can think of enums are a series of defines

enum { THIS, THAT, THE_OTHER_THING };
// is the same as
#define THIS 0
#define THAT 1
#define THE_OTHER_THING 2

The function readWeight() takes a parameter, if the parameter is true, it does the calculations and reporting. If it is false, it just reads the loadcell. chkWeight() calls that function with the parameter as false. getWeight() calls that function with the parameter as true. It helps eliminate redundant code.

As for your problem, I don't know since I don't have your hardware. I did notice in your original code, you were defining 'weight' as a global variable but then also defining it again inside your getWeight() function. That creates a local copy that has nothing AT ALL to do with the global variable. When getWeight() finishes, the local copy disappears and the global variables has not changed. You might have just wanted to assign to the variable?

      float weight = LoadCell.getData();  // declaration of a new variable, followed by assignment  probably wrong
      weight = LoadCell.getData();  // assignment to the global variable, probably what you wanted

As for next steps... Try out the code. See what it does. See what output is produced on the Serial Monitor. Report back if things aren't right. Be sure to include your code (in code tags), any error messages or Serial output (in code tags) what you where expecting and how it differs from what you are seeing.

blh64:
As for next steps... Try out the code. See what it does. See what output is produced on the Serial Monitor. Report back if things aren't right. Be sure to include your code (in code tags), any error messages or Serial output (in code tags) what you where expecting and how it differs from what you are seeing.

I tried using this code but it doesn't work. No movements on LCD nor on serial Monitor also the LCD refresh rate is so high that the characters on it almost appears to be bouncing!
So I decided to continue with my messy code (tho messy it was the last known best configuration my system was working correctly). It turned out that the entire chaos was happening becoz even if the division happens with zero in it, it would give negative result further messing up the next calculations So I did this

void getWeight()
{
  // check for new data/start next conversion:
  if (LoadCell.update()) newDataReady = true;

  // get smoothed value from the dataset:
  if (newDataReady)
  {
    if (millis() > t + serialPrintInterval) 
    {
       weight = LoadCell.getData();
      //Serial.println("Load_cell output val: ");
      //lcd.print(weight);
      //lcd.print("Ltrs");
      if(units > 0 && weight != 0)
      {
        mass = weight * units;
       }
      if(mass > 1000)
      {
        kgs = mass/1000.0;
      Serial.print("Converted weight:");
      Serial.println(kgs);
      lcd.print(kgs);
      lcd.print("Ltrs");
      }
      else
      {
      Serial.print("Weight before unit division:");
      Serial.println(weight);

      Serial.print("Units division:");
      Serial.println(units);
      
      Serial.print("UnConverted weight:");
      Serial.println(mass);
      
      lcd.print(mass);
      lcd.print("Ltrs");
      }
      DisplayFuel();
      newDataReady = 0;
      t = millis();
    }
 }
}

This was working great until my client asked me that this is not exactly what he wants...!
he wants that each time 500,l fuel is poured thru the system procedure, the already present weight should be tared and a new unit factor (500.0/new weight of newly poured 500,l of fuel only) should be drawn which will be used to multiply the poured quantity of fuel from page 3 onwards and later on, the old converted weight and this newly converted weight should be added and displayed on to the LCD!
i don't know if I made it clear enough or not :frowning:
So, I altered the code again like this (file is attached below as it is too long)

but now my system goes on adding the weight even when there's no weight placed on it at all!

I also noticed one thing when I ran HX711_ADC library calibration code, two successive runs gives me two different calibration factor! why? and how? I get as many different calibration factor as many times I run the calibration code after restarting my arduino. When no weight is placed on it I can see the values toggling like 0.16, 0.17 0.10 and so on...also it switches to negative for awhile and comes in positive again and goes back to negative this continues until I place any weight on it.

why this ghostly appearance?

Fuel_Checking_System_V2.3.ino (10.9 KB)

...
    if (millis() > t + serialPrintInterval)
...

:cry:

[quote author=Coding Badly date=1615149004 link=msg=4919348]

...
    if (millis() > t + serialPrintInterval)
...

:'([/quote]
I don't get the meaning of emojis! can you just quote your feelings in normal human words, please? then may be I'll understand what changes I need to make. also, please tell me if not that what should be done. I read millis are non blocking delays in programs

Either that or you could just compare how you handle millis with how @blh64 handles millis in post #2.

Okayy, but that code is not working

[quote author=Coding Badly date=1615175688 link=msg=4919640]
Either that or you could just compare how you handle millis with how @blh64 handles millis in post #2.[/quote]
Okay I edited two of my functions just like the code in #2

void chkWeight()
{
  // check for new data/start next conversion:
  if (LoadCell.update()) newDataReady = true;

  // get smoothed value from the dataset:
  if (newDataReady) {
     if (millis() - lastSerialTime >= serialPrintInterval)  
    {
     weight = LoadCell.getData();
      Serial.print("Load cell output value:");
      Serial.println(weight);
     newDataReady = 0;
     lastSerialTime = millis();
    }
   }
 }

and this one

void getWeight()
{
  // check for new data/start next conversion:
  if (LoadCell.update()) newDataReady = true;

  // get smoothed value from the dataset:
  if (newDataReady)
  {
    if (millis() - lastSerialTime >= serialPrintInterval) {
      weight = LoadCell.getData();
       weight = LoadCell.getData();
       Serial.print("Old Volume (Mass 2):");
      Serial.println(mass2);
      Serial.println();
      if(units > 0 && weight != 0)
      {
        mass1 = weight * units;
       // if (flag)
      //  {
          mass3 = mass1 + mass2;
          mass2 = mass3;
          //flag = false;
       // }
        //previous_mass1 = mass1;
       }
      if(mass3 > 1000)
      {
        kgs = mass3/1000.0;
      Serial.print("Converted weight:");
      Serial.println(kgs);
      lcd.print(kgs);
      lcd.print("Ltrs");
      }
      else
      {
     if (millis() > t + serialPrintInterval) 
    {   
      debug();
    }
      lcd.print(mass3);
      lcd.print("Ltrs");
      }
      DisplayFuel();
      newDataReady = 0;
      lastSerialTime = millis();
    }
 }
}

And now you are handling unsigned variables correctly. The only proper way to deal with time like millis() is by calculating elapsed time as current time - some start time and then comparing it to the interval you are interested in. Your original method of adding something to the current time will break when the current time is near the roll-over point for an unsigned long (about every 49 days). It may not matter in your case, but it is still wrong.

blh64:
And now you are handling unsigned variables correctly. The only proper way to deal with time like millis() is by calculating elapsed time as current time - some start time and then comparing it to the interval you are interested in. Your original method of adding something to the current time will break when the current time is near the roll-over point for an unsigned long (about every 49 days). It may not matter in your case, but it is still wrong.

okay, got it. But the thing is the code doesn't work. The load cell values seems to be stuck and doesn't reflect the change or may be they don't get updated

So what does your latest code look like? You posted original code, I posted some untested code that you say doesn't work. You reverted back to your code, but then modified it yet it still doesn't work. You need to post your code when you change it so people can see what you are using vs. trying to guess.

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