Unstable output for loadcell

I'm using a 10kg loadcell with HX711 module with library HX711_ADC with arduino uno.

Whenever I run the calibration example sketch from the library my each iterations yeilds different calibration factor. Also the weight it displays keeps on toggling in the small amounts (after the decimal point)

I have mounted my loadcell on the edge of the table with G clamp and the arrow is pointing downwards.

What causes this problem?

What is the magnitude of this value? What controller are You using? Use code tags and post the code.

Railroader: What is the magnitude of this value? What controller are You using? Use code tags and post the code.

I'm using the sketch in example of library named calibration. I'm using Arduino UNO

Please post that code here. I don't spend time to find and space to install that library.

Railroader:
Please post that code here. I don’t spend time to find and space to install that library.

/*
   -------------------------------------------------------------------------------------
   HX711_ADC
   Arduino library for HX711 24-Bit Analog-to-Digital Converter for Weight Scales
   Olav Kallhovd sept2017
   -------------------------------------------------------------------------------------
*/

/*
   This example file shows how to calibrate the load cell and optionally store the calibration
   value in EEPROM, and also how to change the value manually.
   The result value can then later be included in your project sketch or fetched from EEPROM.

   To implement calibration in your project sketch the simplified procedure is as follow:
       LoadCell.tare();
       //place known mass
       LoadCell.refreshDataSet();
       float newCalibrationValue = LoadCell.getNewCalibration(known_mass);
*/

#include <HX711_ADC.h>
#include <EEPROM.h>

//pins:
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;
long t;

void setup() {
  Serial.begin(57600); delay(10);
  Serial.println();
  Serial.println("Starting...");

  LoadCell.begin();
  long stabilizingtime = 2000; // preciscion right after power-up can be improved by adding a few seconds of stabilizing time
  boolean _tare = false; //set this to false if you don't want tare to be performed in the next step
  LoadCell.start(stabilizingtime, _tare);
  if (LoadCell.getTareTimeoutFlag() || LoadCell.getSignalTimeoutFlag()) {
    Serial.println("Timeout, check MCU>HX711 wiring and pin designations");
    while (1);
  }
  else {
    LoadCell.setCalFactor(1.0); // user set calibration value (float), initial value 1.0 may be used for this sketch
    Serial.println("Startup is complete");
  }
  while (!LoadCell.update());
  calibrate(); //start calibration procedure
}

void loop() {
  static boolean newDataReady = 0;
  const int serialPrintInterval = 0; //increase value to slow down serial print activity

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

  // get smoothed value from the dataset:
  if (newDataReady) {
    if (millis() > t + serialPrintInterval) {
      float i = LoadCell.getData();
      Serial.print("Load_cell output val: ");
      Serial.println(i);
      newDataReady = 0;
      t = millis();
    }
  }

  // receive command from serial terminal
  if (Serial.available() > 0) {
    float i;
    char inByte = Serial.read();
    if (inByte == 't') LoadCell.tareNoDelay(); //tare
    else if (inByte == 'r') calibrate(); //calibrate
    else if (inByte == 'c') changeSavedCalFactor(); //edit calibration value manually
  }

  // check if last tare operation is complete
  if (LoadCell.getTareStatus() == true) {
    Serial.println("Tare complete");
  }

}

void calibrate() {
  Serial.println("***");
  Serial.println("Start calibration:");
  Serial.println("Place the load cell an a level stable surface.");
  Serial.println("Remove any load applied to the load cell.");
  Serial.println("Send 't' from serial monitor to set the tare offset.");

  boolean _resume = false;
  while (_resume == false) {
    LoadCell.update();
    if (Serial.available() > 0) {
      if (Serial.available() > 0) {
        float i;
        char inByte = Serial.read();
        if (inByte == 't') LoadCell.tareNoDelay();
      }
    }
    if (LoadCell.getTareStatus() == true) {
      Serial.println("Tare complete");
      _resume = true;
    }
  }

  Serial.println("Now, place your known mass on the loadcell.");
  Serial.println("Then send the weight of this mass (i.e. 100.0) from serial monitor.");

  float known_mass = 0;
  _resume = false;
  while (_resume == false) {
    LoadCell.update();
    if (Serial.available() > 0) {
      known_mass = Serial.parseFloat();
      if (known_mass != 0) {
        Serial.print("Known mass is: ");
        Serial.println(known_mass);
        _resume = true;
      }
    }
  }

  LoadCell.refreshDataSet(); //refresh the dataset to be sure that the known mass is measured correct
  float newCalibrationValue = LoadCell.getNewCalibration(known_mass); //get the new calibration value

  Serial.print("New calibration value has been set to: ");
  Serial.print(newCalibrationValue);
  Serial.println(", use this as calibration value (calFactor) in your project sketch.");
  Serial.print("Save this value to EEPROM adress ");
  Serial.print(calVal_eepromAdress);
  Serial.println("? y/n");

  _resume = false;
  while (_resume == false) {
    if (Serial.available() > 0) {
      char inByte = Serial.read();
      if (inByte == 'y') {
#if defined(ESP8266)|| defined(ESP32)
        EEPROM.begin(512);
#endif
        EEPROM.put(calVal_eepromAdress, newCalibrationValue);
#if defined(ESP8266)|| defined(ESP32)
        EEPROM.commit();
#endif
        EEPROM.get(calVal_eepromAdress, newCalibrationValue);
        Serial.print("Value ");
        Serial.print(newCalibrationValue);
        Serial.print(" saved to EEPROM address: ");
        Serial.println(calVal_eepromAdress);
        _resume = true;

      }
      else if (inByte == 'n') {
        Serial.println("Value not saved to EEPROM");
        _resume = true;
      }
    }
  }

  Serial.println("End calibration");
  Serial.println("***");
  Serial.println("To re-calibrate, send 'r' from serial monitor.");
  Serial.println("For manual edit of the calibration value, send 'c' from serial monitor.");
  Serial.println("***");
}

void changeSavedCalFactor() {
  float oldCalibrationValue = LoadCell.getCalFactor();
  boolean _resume = false;
  Serial.println("***");
  Serial.print("Current value is: ");
  Serial.println(oldCalibrationValue);
  Serial.println("Now, send the new value from serial monitor, i.e. 696.0");
  float newCalibrationValue;
  while (_resume == false) {
    if (Serial.available() > 0) {
      newCalibrationValue = Serial.parseFloat();
      if (newCalibrationValue != 0) {
        Serial.print("New calibration value is: ");
        Serial.println(newCalibrationValue);
        LoadCell.setCalFactor(newCalibrationValue);
        _resume = true;
      }
    }
  }
  _resume = false;
  Serial.print("Save this value to EEPROM adress ");
  Serial.print(calVal_eepromAdress);
  Serial.println("? y/n");
  while (_resume == false) {
    if (Serial.available() > 0) {
      char inByte = Serial.read();
      if (inByte == 'y') {
#if defined(ESP8266)|| defined(ESP32)
        EEPROM.begin(512);
#endif
        EEPROM.put(calVal_eepromAdress, newCalibrationValue);
#if defined(ESP8266)|| defined(ESP32)
        EEPROM.commit();
#endif
        EEPROM.get(calVal_eepromAdress, newCalibrationValue);
        Serial.print("Value ");
        Serial.print(newCalibrationValue);
        Serial.print(" saved to EEPROM address: ");
        Serial.println(calVal_eepromAdress);
        _resume = true;
      }
      else if (inByte == 'n') {
        Serial.println("Value not saved to EEPROM");
        _resume = true;
      }
    }
  }
  Serial.println("End change calibration value");
  Serial.println("***");
}

This one

Thanks. I would like to look closer at the ADC conversion used. That depends on the controller You use. F ex. an UNO, or a Mega, use a 10 bit converter giving values from 0 to 1023. That means the resolution is 1/1023.

You write Also the weight it displays keeps on toggling in the small amounts (after the decimal point)

Tell what the value is. Lets say it is 10 kg. Then a variation like 10.00 to 10.01 is unavoidable. Any little electrical noise easily gives a swing like from 10.00 to 10.05, 10.10.

Railroader: Thanks. I would like to look closer at the ADC conversion used. That depends on the controller You use. F ex. an UNO, or a Mega, use a 10 bit converter giving values from 0 to 1023. That means the resolution is 1/1023.

You write

Also the weight it displays keeps on toggling in the small amounts (after the decimal point)

Tell what the value is. Lets say it is 10 kg. Then a variation like 10.00 to 10.01 is unavoidable. Any little electrical noise easily gives a swing like from 10.00 to 10.05, 10.10.

I use HX711 module for the ADC its resolution is higher than UNO, yes the values after decimal point swings like with 4-5 digits...in last... just the way you mentioned! like if it is 100.12 sometimes and 100.16 or even 99.98-99.99 sometimes(but this one is quite rare) if its becoz of noise is there any way to filter that out? like the one we see in the weighing scales in market?

Thanks for Your reply. It gives valuable information.

A 24 bit ADC is a highly advanced and capable device. To use that resolution is almost impossible. It call for noise suppression all over the place, but I think things can be done.

1) All digital circuits should have a "bulk" capacitor of some 10 to 100 microfarads, depending on their current consuming way, as well as a fast ceramic capacitor att the plus power input, Vcc. This will bring down noise that could be sent out from that circuit and hit the ADC.

2) The analog signal from the load cell can do well having some ceramic filtering capacitor on it. Too large caps will affect fast changes, quick weight changes to be handled well. Too small will not be enough. I suggest testing in the range nanoFarads, 1, 10, maygbe 100.

Railroader: Thanks for Your reply. It gives valuable information.

A 24 bit ADC is a highly advanced and capable device. To use that resolution is almost impossible. It call for noise suppression all over the place, but I think things can be done.

1) All digital circuits should have a "bulk" capacitor of some 10 to 100 microfarads, depending on their current consuming way, as well as a fast ceramic capacitor att the plus power input, Vcc. This will bring down noise that could be sent out from that circuit and hit the ADC.

2) The analog signal from the load cell can do well having some ceramic filtering capacitor on it. Too large caps will affect fast changes, quick weight changes to be handled well. Too small will not be enough. I suggest testing in the range nanoFarads, 1, 10, maygbe 100.

Okay so it seems to be more of a hardware issue than a firmware issue. I tried adding 0.1uF capacitor ceramic on the VCC and GND pins of HX711 module but it didn't had any positive or negative effects. I'm powering up my system through USB right now, but I have a provision of powering up my system through external power supply, there I have a 1000uf/50V capacitor and some two to three 0.1uF ceramic capacitors. But it appears to me that USB power is well cleaned power especially if comes from Laptop, right? but still I'm having those issues.

You need to define the range and precision that you are seeking.
As its a 10kg load cell I’d assume you need to measure in the range ± 10kg. (or 0 - 10kg)
You havent linked to the load cell spec so we dont know which you are using.

I’d expect you have used the HX711 red, black, white green and yellow connections to your load cell as that provides a very stable excitation supply to the load cell.

And the board has on-board smoothing and decoupling for the psu.

So what sources of noise are left? Well you could have a noisy load cell; or noisy connections (you HAVE soldered them I hope?)
Then vibration and draughts could affect your readings.

So what is the precision you need? Do you need a result that is accurate to 1g?

And what readings are you getting - for zero, and with a test weight (eg 1kg) added.

You can add a little hysteresis in your program to reduce the effects of noise.

johnerrington: You need to define the range and precision that you are seeking. As its a 10kg load cell I'd assume you need to measure in the range +- 10kg. (or 0 - 10kg) You havent linked to the load cell spec so we dont know which you are using.

I'd expect you have used the HX711 red, black, white green and yellow connections to your load cell as that provides a very stable excitation supply to the load cell.

And the board has on-board smoothing and decoupling for the psu.

So what sources of noise are left? Well you could have a noisy load cell; or noisy connections (you HAVE soldered them I hope?) Then vibration and draughts could affect your readings.

So what is the precision you need? Do you need a result that is accurate to 1g?

And what readings are you getting - for zero, and with a test weight (eg 1kg) added.

You can add a little hysteresis in your program to reduce the effects of noise.

I'm using 10kg load cell. I'm expecting precision uto 1gms. I have soldered the connections. I'm using red, black, white and green wire with yellow unconnected since I didn't knew where the yellow wire should fit in. Where do I need to connect yellow wire? If it adds to Stability then I will have to definitely give it a shot.

Suggest you read this: https://www.analog.com/en/analog-dialogue/articles/a-reference-design-for-weigh-scales.html#

All of it, but especially the sections on "Improving the ADC result," "Improving the response time to changes," and "removing flicker."

Keep in mind the article is directed at commercial developers of scales and that hobbyists using mid- to low-end equipment may have more difficulty solving the various problems mentioned in the article.

If you really need an accurate 10kg scale with 1 gram resolution, you are probably better off buying a commercial product. If you need to communicate with it, get one with RS-232 or 485, bluetooth, I2C, SPI, or USB capabilities.

What causes this problem?

I would try a higher SAMPLES setting - the default is 16. You could try 32, 64 or 128. In your library, go to config.h line 27.

Also, your printing activity is set as high as possible (0) and this might be triggering the issue as described lines 38-40 of config.h. I would try slowing down print activity here to maybe every 500ms?

const int serialPrintInterval = 500; //increase value to slow down serial print activity

I suggest a little test. Use a standard alkaline battery, if needed a voltage divider, and apply that signal to the controller, instead of the the load cell signal. Use as short cables as ever possible. Does that give You the stable readings You want? In other words, does the error come from the load cell stuff or from the surrounding circuitry?