HX711 Calibration

Hi,
I am currently in the process of calibrating my load cell, and initially I decided to calibrate with a 1kg mass. I got a calibration factor of around 48400, however, I noticed that if I start adding more weight it begins to deviate from the true value.
The calibration factor for 2kg is 50500, and 51500 for 3kg.

Is there a reason for this deviation, and is there anything I can do to avoid it from happening?

Here is the code I used for calibration:

#include "HX711.h"

const int LOADCELL_DOUT_PIN = 2;
const int LOADCELL_SCK_PIN = 3;

HX711 scale;

float calibration_factor = 48400; 

void setup() {
  Serial.begin(9600);
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale();
  scale.tare(); 

  long zero_factor = scale.read_average(); //Get a baseline reading
  Serial.print("Zero factor: ");
  Serial.println(zero_factor);
}

void loop() {
  scale.set_scale(calibration_factor);

  Serial.print("Reading: ");
  Serial.print(scale.get_units(), 1);
  Serial.print(" kg");
  Serial.print(" calibration_factor: ");
  Serial.print(calibration_factor);
  Serial.println();

  if(Serial.available())
  {
    char temp = Serial.read();
    if(temp == '++' || temp == 'a')
      calibration_factor += 1000;
    else if(temp == '--' || temp == 'z')
      calibration_factor -= 1000;
    else if(temp == '+' || temp == 'b') 
      calibration_factor += 100;
    else if (temp== '-' || temp == 'x')
      calibration_factor -= 100;
  }
  delay(1000);
}

This explains how to calibrate:

It should be clear why calibration changes with weiht

I would run the hx711 library example code first to check basic operation ..
How the weight is connected to the load cell is important as the geometry may affect linearity .
You also need to calibrate with a weight that is at least 50% of its span value .
Need drawings /picture and wiring diagram

A calibration involves 2 known points to obtain gain nd offset. You have only one. Make another point for 3kg. Apply the gain and offset in the equation to know the actual weight of the unknown mass. You may try the following sketch:

/* This program takes 10 samples from LC + HX711B at
   2-sec interval and then computes the average.
*/

unsigned long x = 0, y=0;
unsigned long dataArray[10];
int j = 0;
void setup()
{
  Serial.begin(9600);
  pinMode(A1, INPUT); //data line  //Yellow cable
  pinMode(A0, OUTPUT);  //SCK line  //Orange cable
}

void loop()
{

  for (int j = 0; j < 10; j++)
  {
    digitalWrite(A0, LOW);//SCK is made LL
    while (digitalRead(A1) != LOW) //wait until Data Line goes LOW
      ;
    {
      for (int i = 0; i < 24; i++)  //read 24-bit data from HX711
      {
        clk();      //generate CLK pulse to get MSB-it at A1-pin
        bitWrite(x, 0, digitalRead(A1));
        x = x << 1;
      }
      clk();  //25th pulse
      Serial.println(x, HEX);
      y = x;
      x = 0;
      delay(1000);
    }
    dataArray[j] = y;
  }

  Serial.println("===averaging process=========");
  unsigned long sum = 0;

  for (j = 0; j < 10; j++)
  {
    sum += dataArray[j];
  }
  Serial.print("Average Count = ");
  sum = sum / 10;
  Serial.println(sum, HEX);
 // float W = (float)0.90*(sum-901002)/946560 + 0.75;//0.005331 * sum - 1146.176;
  //W = (float)W / 1000.00; 
 // Serial.println(W, 2);
}

void clk()
{
  digitalWrite(A0, HIGH);
  digitalWrite(A0, LOW);
}

Setup:

1 Like

Wrong, he calls tare() to set the offset.

He uses a tested library, this simple chip testing sketch doesn't help him.

1 Like

Do you want to claim that the said load cell (the input device as a whole) is a pseudo ideal system whose gain is unity?

All practical devices are real devices (majority of them are linear) and they need to be artificially aligned (calibrated response, Fig-1) along the response line of an ideal system through 2-point calibration.


Figure-1:

Looks like a non-linearity in your cell of about +/-3% in the 1-3kg range. What are the specs on your load cell?

With multiple known calibration weights, you could fit a higher-order curve that matches the calibration weights exactly.

Edited:

With your data:

weight (kg) reading (raw)
0 0
1 48400
2 101000
3 154500
1 Like

Apologies I probably should have provided more context. I'm using a 1kg load cell, and I followed the Load Cell Amplifier HX711 Breakout Hookup Guide and I am using the library found here: GitHub - bogde/HX711: An Arduino library to interface the Avia Semiconductor HX711 24-Bit Analog-to-Digital Converter (ADC) for Weight Scales..
I'm a bit new to all this so I definitely might have missed something but I was under the impression that it should be linear, but maybe it's just happened to be that I selected weights where it deviates slightly? I'm not sure

What makes you think it isn't

OK, a 2 and 3kg on a 1kg load cell would be out of spec, and could push it out of the linear range so a simple, linear, two-point calibtration isn't accurate. I'd calibrate it with your 1kg weight.

If you want to experiment with overloading it, I'd leave it calibrated with the 1kg, measure the 3kg, and then see if the 1kg still reads 1.000kg, and the 3kg measures whatever it does repeatably. If the numbers are repeatable, you could use a fancier/higher-order calibration to interpolate between the values.

The load is practically linear and can be easily verified by plotting the responses at known 3 or more known points (0 gm, C0; 250 gm, C1; 500 gm, C2; 1000 gm, C3) through the execution of the sketch of post #4. You simply find the gain and offset and place them in y = mx + k equation.

1 Like

With the above data, I have found the following equation:
w x 105 = 1.86 x c + 0.11 x 105

1. After putting c = 48400, we get:
w = 0.91 + 0.11
==> w = 1.02 (error is: 2%. Is it acceptable?)

2. After putting c = 101000, we get:
w = 1.89 + 0.11
==> w = 2.00 (error is: 0%)

3. After putting c = 0, we get:
w = 0 + 0.11
==> 0.110 (unacceptable 110 gm error)

Why use a 2-point linear fit?

With this data I find this quadratic fit:

weight = 3.917567e-03 +  2.080260e-05 * raw + -9.270636e-12 * pow(raw,2)

and this 4-point cubic equation fit:

weight = 9.296695e-17 +  2.187317e-05 * raw + -2.921433e-11 * pow(raw,2) +   8.621264e-17 * pow(raw ,3)

Either would fit the calibration weights better than a linear fit.

> xdf
  w    raw
1 0      0
2 1  48400
3 2 101000
4 3 154500
> model1 = lm(w ~ raw  , xdf)
> model2 = lm(w ~ raw + I(raw^2) , xdf)
> model3 = lm(w ~ raw + I(raw^2) + I(raw^3), xdf)
> summary(model1)

Call:
lm(formula = w ~ raw, data = xdf)

Residuals:
       1        2        3        4 
-0.02868  0.03401  0.01537 -0.02070 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 2.868e-02  3.005e-02   0.954 0.440656    
raw         1.937e-05  3.150e-07  61.480 0.000264 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.03636 on 2 degrees of freedom
Multiple R-squared:  0.9995,	Adjusted R-squared:  0.9992 
F-statistic:  3780 on 1 and 2 DF,  p-value: 0.0002645

> summary(model2)

Call:
lm(formula = w ~ raw + I(raw^2), data = xdf)

Residuals:
        1         2         3         4 
-0.003918  0.010954 -0.010410  0.003374 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)  
(Intercept)  3.918e-03  1.548e-02   0.253    0.842  
raw          2.080e-05  4.894e-07  42.504    0.015 *
I(raw^2)    -9.271e-12  3.029e-12  -3.060    0.201  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01597 on 1 degrees of freedom
Multiple R-squared:  0.9999,	Adjusted R-squared:  0.9998 
F-statistic:  9800 on 2 and 1 DF,  p-value: 0.007143

> summary(model3)

Call:
lm(formula = w ~ raw + I(raw^2) + I(raw^3), data = xdf)

Residuals:
ALL 4 residuals are 0: no residual degrees of freedom!

Coefficients:
              Estimate Std. Error t value Pr(>|t|)
(Intercept)  9.297e-17        NaN     NaN      NaN
raw          2.187e-05        NaN     NaN      NaN
I(raw^2)    -2.921e-11        NaN     NaN      NaN
I(raw^3)     8.621e-17        NaN     NaN      NaN

Residual standard error: NaN on 0 degrees of freedom
Multiple R-squared:      1,	Adjusted R-squared:    NaN 
F-statistic:   NaN on 3 and 0 DF,  p-value: NA

> predict(model1)
        1         2         3         4 
0.0286801 0.9659869 1.9846302 3.0207028 
> predict(model2)
          1           2           3           4 
0.003917567 0.989046191 2.010410001 2.996626241 
> predict(model3)
           1            2            3            4 
9.296695e-17 1.000000e+00 2.000000e+00 3.000000e+00 

1. How have you found the co-efficients?
2. With raw = 0, how pow(0, 2) is going to be evaluated -- I mean 02 = ?

  1. I used R, per the snippets of transcripts in #13. Instead, one could use Excel, add a couple X^2 and X^3 columns and use the Data Analysis/Regression tool.

  2. pow(0,2) = 02 = 0*0 = 0.

Amazing!!

How could be that I could not conceive this method though I was trained in 1979 at the age of 25 by a British Training Engineer (P.J. Erret at GEC Kidsgrove, UK) to think in a "simple way"?

You were probably thinking of pow(x,y) using log(x). I had to check that it worked with zeros when you asked.

pow() on AVR does a lot:

Having looked at "0 as raw reading (post #13)" and then seeing "pow(raw, 2)" in your equation, my mind immediately recalled its learning that "something will be raised to the power of 2"; but, 0 is not something; so, how 02 would be evaluated?

I have verified that the expression pow(0, 2) is evaluated to 0, and (probably) it follows your opinion (0 x 0) which agrees with the "argument combination" of the referred file of GitHub.

The tricky one is 00.

In mathematics, it is undefined. In programming, there are conventions.

Any non-zero number raised to the power of 0 is 1. You can see the problem that develops around 0.

a7