Load Cell (weight) questions

I imagine the Harbor Freight scale does the tare and overload calculations in software, and it is not a physical limitation at 1.000kg/35.274oz. Above, I suggested loading it to 2kg, and then unloading it to see if it measured as normal. It may not be 0.1 gram/0.003oz accuracy outside of the rated range, but it might be sufficient for your 0.25oz needs.

I think this is a good goal.

If you have a DMM that can measure millivolts, you may be able to test the voltage with the loading. You could measure the voltage unloaded, with 1kg, and with 2kg to get some confirmation of the range and linearity.

With a two-point calibration of zero and a calibration weight, it is good to use a weight near full scale, or a weight similar to the normal items that you would be weighing. I wouldn't use a single nickel, because the normal operating range sounds more like 1500gm or 50oz, and extrapolating from a 0-5gm calibration would magnify any errors.

As for the units, it would be in the units you want the scale to display. The HX711 libraries have one scaling factor, and they use it to transform the internal ADC readings of the amplified wheatstone bridge voltage signal into user-units. If you have a 1kg weight, you could put in 1.000 for kg, or 1000 for grams, or 35.274 for ounces, and the calibration script would figure out the calibration to multiply the values by that would give you the chosen units:

Ahh. I see. Yes, I believe that is the behavior. So I am understanding your stance now (software limiting, but physical bar could probably do more than 1kg)

Annnnd. Ok, sold. Why not try? $12.00 for testing.. meh.. ok. :slight_smile: I don t need 0.003oz accuracy.. 0.10oz would be nice.. but 0.25oz is the current minimum allowed minimum ingredient weight in the recipe builder/page, so yeah 0.25oz as accurate is needed.

Side question: Is this fast enough? As output of liquid happens *(and having to prepare for the extra flow/output after the stoppage), I will need to stop a bit ahead of time... but never really thought about the 'speed' of the weight updates?

So do not use a 'nickle' as it its too small of weight your saying? (was only thing I knew was supposed to be a certain weight to compare if things were 'working') Sorry what is '2' point calibration? Are you referring to more than one load sensor now? (or just multiple/2-time calibration attempts?)

Lastly.. ther are so many libs.. and many tutorials (some using old libs as well).. What lib should be used? Is there a popular/favorite? Should I stick with HX711_ADC lib? Just want to stay on path so answers make more sense... and code is referred to as the same..etc

Some morning/family stuff.. and then the HF scale will be hacked.. I'll post back later!

Thanks

Two point calibration is setting the zero point and the calibration weight point, and assuming it is linear in between. If it shows zero for zero, and 50oz for a 50oz weight, then it’s probably good for weights in-between. Lots of scale code is setup for a hard coded calibration constant, and you do a 1-point calibration on startup to set the zero, tare, or the intercept point. But one would need a two point calibration to determine the slope or calibration Constant to use for future 1-point calibrations

(late) update.

I finally got around to hacking the HF scale apart, and connecting it to the Arduino via the HX711 amp board.

  • scale was powered by 2 x AAA batteries (via the internal PCB).. then output to the 4-wires to the load cell bar (I didnt check it before cutting wires)

  • current set-up is default Arduino (+5v/GND - A/4/A5 for the digital I/O pins)

I am -not- running a good/current/valid calibration (still need to walk through/figure out those exact steps).. but when placing something on the scale after entering 0.5 in the calibration value/setting.. it does in fact show differences. (so wiring/transfer seems to be valid/working)


update 1:

I believe you are/were correct, this load cell sensor/bar' from the HF scale can handle more weight that declared.. based on the pcb hardware/software. I have been loading it with random things.. (adding) and even pressing a bit.. I see the number change/climb in the serial monitor.

ok.. so now, calibration. Just so I am clear: "Should I stick with HX711_ADC lib?", and run their calibration script? Sorry I'm still confused/not clear on your answer above about the default 'weight' type/unit it is being asked for? (I think I get what you are saying that if I want to convert from different unit types, I can do so in the script somewhere? (but you linked to the class?)

So what am I to do? (sorry) when I run the script, it ask for an input value of known mass... I understand you said dont use a nickle, and to use more of a the 'max weight' of what will be weighed (the drink).. but am I entering that in as......grams then?

What I did: I weight some random objects, that came out to '115.5' grams. I then ran the calibration script on the Arduino.. waited for it load/run .. I added the same items. add 115.5 and hit enter...

I then removed and got 0 (more or less, anywhere from 0.01 to -0.01)... and then added the nickle again( (sorry!) LOL.. and I saw '5.04' in the serial monitor.

Not sure where to go from here?

Question: Speed? When using it as a 'scale' (or using another one small one to compared outcomes to the Arduino connected one now).... the output/lCD screen as super fast. Why is the serial monitor approach so.....slow? You see the count up/down the whole things? Is this a code/approach thing only perhaps? Where as these Arduino examples are looping/doing serial output which slows things down, where as the default/normal scale maybe just.... do a slower/timed approach? Looking at the serial monitor/out, trying to catch 0.25oz output,, you would be very 'late'.. lol Either way, so far a step in the right direction. (hardware and approach/understanding wise)

Thanks so far for the help!

Yes, if you want results in grams, then you would enter your known mass's mass in grams.

I'm assuming you are using something like this calibration script in:

/*
   -------------------------------------------------------------------------------------
   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>
#if defined(ESP8266)|| defined(ESP32) || defined(AVR)
#include <EEPROM.h>
#endif

//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;
unsigned long t = 0;

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

  LoadCell.begin();
  //LoadCell.setReverseOutput(); //uncomment to turn a negative output value to positive
  unsigned long stabilizingtime = 2000; // preciscion right after power-up can be improved by adding a few seconds of stabilizing time
  boolean _tare = true; //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) {
    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) {
        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("***");
}

Speed can depend on the RATE input pin to the HX711 chip (10Hz vs 80Hz), and also on the averaging done in the code. How fast do you need to read?

Thanks for the reply/follow-up.

  • I do ultimately want things in oz (ounces) vs grams... but wasnt clear what for unit type was to be entered.. but I guess doesnt matter the unit type. Just use the known mass/weight of the object itself.

  • Interesting.. I am using the HX711-ADC lib.. (even when I just clicked update).. the Calibration script states its from... 2017? Where as your example is from 2021?)

RE: Speed. First Ive heard about a RATE pin? (this from the sensor to the HX711? or from Arduino to HX711 board?) How can I tell if it is 10hz vs 80hz? Is there any control over that aspect of things?)

I guess I'm not sure how to answer the last question (sorry). As fast as possible I guess?

When using this to 'measure/weight' (control) the output of liquid to create drink recipes... I would think faster the better? To be more accurate on the output? This whole topic is more or less a 'band-aid' fix to a completed/running project that was converted over to using pumps. Pumps are not very accurate, especially when compared to other pumps of same brand/model. Timing of pressurized solenoid valves does not work same (repeatability) as trying ti time the on/off time for a pump (output is not consistent)

So if a recipe has an ingredient of say 0.25oz of juice... pump needs to -accurately- output only 0.25 of liquid. I'll be altering the snippet/loop of code now to: (more or less)

tare scale/weight
turn on pump
start weighing glass (stuck in this linear 'loop' until target weight has been measure, and then move on,....once certain ingredient amount has been reached, turn pump off. Nice part of this project is everything is linear, one behavior at a time..)

  • of course a little planning ahead of turning if off BEFORE 0.25 is actually reach will be needed (since output will still happen after pump is off,, so planning for overflow..etc).. but thats just code/adoption

I'll upload the latest sketch you posted...

Send 't' from serial monitor to set the tare offset.
Tare complete
Now, place your known mass on the loadcell.
Then send the weight of this mass (i.e. 100.0) from serial monitor.
Known mass is: 170.00
New calibration value has been set to: 2235.52

  • put my phone on an old scale.. stated 170g. put it on calibration Arduino/scale..
  • did TARE
  • type 170, hit enter (are we supposed to save to eeprom each time?)

Now I'm not clear is 'rifting/drifting' is what I'm seeing? But I see more or less same numbers.. maybe off/different by a couple 0.0x values? if I remove phone..sometimes stays @ 0.01 or 0.02.. then maybe goes to 0 or not.

If I place phone again.. sometimes it shows 169.98 or something..

Watching each value output to the serial monitor, reveals things are sloooow. I do understand that serial put also adds time. (but I'm not sure how the default lcd screens/board that were in these scales are so fast? If I take one of the default scales.. drop something on it.. under 1 second you have a final weight displayed. Where as Arduino/serial monitors takes many many seconds.

So I guess at this point, summary: (thanks you and everyones posts)

  • HF scale does work with Arduino/HX711 boards
  • HF scale does seem that it will cover my max weight needs (so far)
  • physical set-up is more beneficial with a smaller load cell bard sensor and the re-purposed scale housing/metal platform

Whats left?

  • not sure if calibration is legit? Needs more work? Fine tuning? (nor how to apply it)
  • is this still worth the R&D is speed is not very very quick

I'm not worried about implementing things in my current sketch/project... but I def need to get more up to speed on the class/lib options (function/methods available)....and should be used for my needs/project...etc

Suggestions on next steps? Anything to try/test out for feedback?

Thanks.

Off of work in like 2.5 hours.. so I can focus more and provide more info if needed!

If you want ounces, use the ounces of your known mass. If you have a 170gm phone you could use 6oz and instead of 2235.52, the calibration value would be set to maybe 2235.52*28.3495= 63375.87 It is just a factor between the internal HX711 ADC count versus the output measurement.

The 0.01 or 0.02gram difference is noise, and is very small compared to your 0.25oz requirements. The noise/error could come from a number of sources, including creep or drift. Creep is the sensor sort of settling into its new position and relaxing. Drift might be changes over a longer time scale.

Sorry, HX711-ADV? I had misremembered your library -- I haven't compared them, but all the libraries should do about the same stuff--negotiate with the HX711 chip and make the results more user friendly. Any one that serves your needs should work.

As for rate, the HX711 has a datasheet:

with this 10SPS or 80SPS rate selectable by the voltage on the RATE pin:

Many breakout boards do not make this pin available on the headers, but some folks modify those boards to choose the rate. The Sparkfun board has a jumper you could cut or solder to get a 80SPS or 10SPS rate:

image

image

First sorry.. (it was a typo above)

I am using the HX711-ADC (not ADV) library (1.2.12?). (in my IDE it loads latest version from

OK.. so, I started over.. used my phone, it weights 7.000 oz

I started the HX711-ADC calibration script again:

Should I be saving to the eeprom then?

Also.. do I need to save this value of: 54504.42 for anything? Is this to be used in/for other sketches?

RE: RATE.

OK.. so the next step is get things to run at 80SPS then? Perhaps ordering one from Sparkfun..

or maybe altering the current board?
I saw this:

my board is similar..but not 100% exactly the same, my pin 15 seems to not be connected to anything? (attached pic)

So.. can I just attach that to +5v,,, and then we have 80SPS rate to start working with? (or do you think I should still 'lift' the pin?)

Current status is: I have measured a few items on a separate (small) scale. (my phone, spray paint can, screw driver.. and then placed them on the hacked/Arduino (HX711 based) scale.. and it repeats the same end weight as from the retail scale.

However it seems very slow. By the time 0.25oz (or even 0.15oz is 'detected' (weighed).. it most likely be way more than 0.25 output in reality due to the slow output/checking of the current mass.

thanks!

I think that library, if you save it to the EEPROM with the calibration script, then the other programs have the option of reading the calibration value back from EEPROM to use it.

The 54504.42 is the constant that converts the HX711 interal ADC counts to Ounces for your setup. If you had a script that used a hard-coded calibration value, you could use the 54504.42 number to convert the raw ADC readings to Ounces.

Rate-wise, I'd try things at the default10SPS unless it is inadequate. I think the RATE pin is probably grounded under the chip somehow, and before you shorted it to VCC, I'd make sure is isn't already shorted to ground. If it is, you'd need to lift it off the ground pad, maybe like the video.

By default, the library averages together the last 16 samples, so it will be slow to respond, since it takes 16/10Hz=1.6 seconds for the old samples to expire. Per the README on the library You can speed things up by reducing the number of samples in the average to:

  • Moving average data set of 1, 2, 4, 8, 16, 32, 64 or 128 samples (default:16).

It is changed to the fastest/lease smoothed with the setSamplesInUse(1) command:

Thanks..
uncommenting and adding this line does seem to speed up the serial monitor output now for sure:

 EEPROM.get(calVal_eepromAdress, calibrationValue); // uncomment this if you want to fetch the calibration value from eeprom

  LoadCell.setSamplesInUse(1);

not sure if that needs to be sent before LoadCell .begin()? Either way, it jumps to 0.00 or to 7.00 (when adding my phone) much quicker. Not sure if it is quite fast enough. (still kinda curious as to how fast 80SPS gets us?.. at 80SPS,... could we bump things back up to 16 (default) samples used? (still faster than 10SPS?) LOL.. although currently this is -not- that bad.. and much better than the default for sure.... I think for detecting 0.25oz (actually less to accommodate for the leftover liquid in the tubes that will empty out)... we might need to actually be faster? Thoughts? noob reaching too far? LOL :slight_smile:

If we do try the 80SPS.. (so I'm clear) .. I just to add some flux, lift the pin off pad (so no more GND connection).. and bridge it to the pin above it? or can I bridge it directly the +5v pin the Arduino connects to?

Thanks. I appreciate the time/lessons here. I feel much better about things.. *(physical size, implementation/mounting, knowing it will cover more than the box says, and hopefully any max mass my project might need to deal with now) (funny/ironic the end solution came from hacking apart a Harbor Freight scale.. LOL.. instead of a pre-purchased kit)

As the hardware side seems to be winding down... I guess I need to start learning more about the class itself. (what functions are there, when.why to use them...etc). With your guidance so far the HX711-ADC lib has been fine (just calibration).. but I loaded the 1x sensor cell sketch and it seems to mirror the same weight results for the known objects.

Since you have much more experience using these.. are there any methods/functions I should be aware of? In my project, once the stepper motors meets its target/end point.. it will:

  • turn pump on (or do you think I should should start scale/weight checking first? I was thinking to possibly avoid any 'splash impact' that could set a false positive of it being at the final 'weight'?)
  • start scale checking.. once the dynamic weight/amount has been met (adjusting for overflow/left-over liquid)
  • turn off pump

In my mind.. I would just TARE() each time before I start a 'weight check'? since the current weight makes no difference, its focus is only the current ingredient amount/output is met. (still need to look at the code better, and see what is all needed for start-up/initialization..etc.. as of now blindly running sketches, and focused on the hardware/calibration stuff)


Tuesday update:

Sorry.. I was feeling antsy..LOL.. I went ahead and lifted pin 15 from the PCB... then bridged it to the pin above it.

Running the read test again.. I see:

:slight_smile: So we are now 80SPS compliant. Serial output seems much faster as well?