Need help with counting scale project

Hi all.

I am a relatively new Arduino user. I have done many of the basic tutorials, and I have created a project that activates a servo based on tripping a PIR sensor.

I am working on a project to make a scale that I can set objects on, and the scale will detect each new object (after a zero reading), count the number of items weighed, keep track of the cumulative total, and then provide a running average weight based on those values.

This is my first post; hopefully I will format it correctly and provide all the data needed.

I am using an Arduino Nano from REXQualis. It is an ATmega328P and CH340 chip. I can program it with the Arduino IDE fine. I am using an HX711 board and 1KG load cell:

I wired my project according to this video:

I am using the HX711_ADC-master library.

My HX711 board does not appear to have a ground plane and testing with a multimeter does not show continuity between E- and GND. I have read some threads here that indicate that this might cause problems, but I'm not clear on how the problem might manifest itself.

I can communicate with the load cell and I am getting reasonable results that closely match a known weight.

I have a few problems that I need help with.

First, the scale does not return zero at no load. Even when I press T to tare the scale, values are close to zero but not zero, and they are not consistent, they dance around. This is not a horrible problem, because I am going to assume that any value under 100 grains (about 6 grams) is an empty scale.

Likewise, when I put a known weight on the scale, it does not repeatably give the same output value. It is close, but again, it dances around. This is bad, because when I put the same object on the scale I would expect the same value to be returned every time.

My biggest problem, however, is trying to determine when the scale has stabilized, so that the value I then capture is the real weight of the object set on the scale.

What I am noticing is that when I set an object on the scale, it "ramps up" to the value, and then when I remove it, it "ramps down". This is a problem because the way my code is set up as soon as the value is over 100 grains, it captures that value as the weight. But the scale is only then on its way up to the real weight.

You can see the output of my code from Serial Monitor here:

You can see that when I set an object on the scale, it first reports as too light, and then when it gets inside the OK range it reads as OK.

I tried to built a "stabilization" factor in but I don't think it's working. I basically said if the absolute value of the previous weight - the current weight is > .25 grains then keep weighing. But even if it worked right this is still a poor solution because the weight could be off by .25 grains.

What I'd like is a way to get the final, stabilized weight of the object in between zero conditions.

Thanks in advance.

Here is my code:

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

/*
   Settling time (number of samples) and data filtering can be adjusted in the config.h file
   For calibration and storing the calibration value in eeprom, see example file "Calibration.ino"

   The update() function checks for new data and starts the next conversion. In order to acheive maximum effective
   sample rate, update() should be called at least as often as the HX711 sample rate; >10Hz@10SPS, >80Hz@80SPS.
   If you have other time consuming code running (i.e. a graphical LCD), consider calling update() from an interrupt routine,
   see example file "Read_1x_load_cell_interrupt_driven.ino".

   This is an example sketch on how to use this library
*/

#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;
float weight_grains = 0; // Variable for holding the weight in grains.
int Counter = 0; // Counter for items being weighed.
float CumulativeWeight = 0 ;// Cumulative weight of items being weighed.
float LastWeight = 0; // Variable for the weight of the previous item weighed.
const float StabFactor = .25 ; // The tolerance for determining if the weight has stabilized or not, in grains.
int NewScaleStatus = 0; // A flag to know whether the state of the scale has changed.
int OldScaleStatus = 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
  float calibrationValue; // calibration value (see example file "Calibration.ino")
  //calibrationValue = 696.0; // uncomment this if you want to set the calibration value in the sketch
#if defined(ESP8266)|| defined(ESP32)
  EEPROM.begin(512); // uncomment this if you use ESP8266/ESP32 and want to fetch the calibration value from eeprom
#endif
  EEPROM.get(calVal_eepromAdress, calibrationValue); // uncomment this if you want to fetch the calibration value from eeprom

  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()) {
    Serial.println("Timeout, check MCU>HX711 wiring and pin designations");
    while (1);
  }
  else {
    LoadCell.setCalFactor(calibrationValue); // set calibration value (float)
    Serial.println("Startup is complete");
  }
}

void loop() {
  LoadCell.setSamplesInUse(1);
  while (abs(CheckScale() - weight_grains) > StabFactor) {
    weight_grains = CheckScale();
    delay(5000);
    Serial.println("stabilizing1...");
  }


  if (weight_grains > 100) {
    if (weight_grains < 500) { // object is too light.
      NewScaleStatus = 1;
      if (NewScaleStatus != OldScaleStatus) {
        Serial.print("Weight is too light! ");
        Serial.println(weight_grains);
      }
      OldScaleStatus = NewScaleStatus;

    } else if (weight_grains > 600) { // object is too heavy .
      NewScaleStatus = 2;
      if (NewScaleStatus != OldScaleStatus) {
        Serial.print("Weight is to heavy !");
        Serial.println(weight_grains);
      }
      OldScaleStatus = NewScaleStatus;

    } else { // object is OK.
      NewScaleStatus = 3;
      if (NewScaleStatus != OldScaleStatus) {
        Serial.print("Weight is OK!");
        Serial.println(weight_grains);
      }
      OldScaleStatus = NewScaleStatus;
    }

  } else { // scale is empty.
    NewScaleStatus = 4;
    if (NewScaleStatus != OldScaleStatus) {
      Serial.println("Scale is empty.");
    }
    OldScaleStatus = NewScaleStatus;
  }

  // receive command from serial terminal, send 't' to initiate tare operation:
  if (Serial.available() > 0) {
    char inByte = Serial.read();
    if (inByte == 't') LoadCell.tareNoDelay();
  }

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

}

float CheckScale() {
  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();
      newDataReady = 0;
      t = millis();
      weight_grains = (i * 15.4324);
      //Serial.print("Load_cell output val: ");
      //Serial.println(weight_grains);
    }
  }
  return weight_grains;
}

Thanks for a quite good posting. Links to sales sights tell the price but don't always present the information kept in a real datasheet. Never mind.
Some variation in the reading is normal. Use decoupling capacitors att the Vcc, Vdd of the circuits.
I suggest You should sample the weight reading and compare the value with earlier samples. As long as the readings show variations greater than a certain amount, keep on sampling.
Looking for a maximum can be one way to go. When the readings decrease a certain amount, use the maximum collected earlier.

If You post the schematics it might give You even better replies.

Here is my schematic:

Here is a picture of the Nano:

I notice in this fellow's video:

He is using a library that allows the call get_units(X) where you can have the unit return the average of X measurements. I don't see that option with the HX711 library I am currently using. Maybe that would help.

In the above video, the fellow is inputting a character to alert the code that it is ready for a measurement with something on the scale.

Perhaps after I detect a "non-zero" measurement (say > 100 grains) I could just hard-code in a delay of some seconds and then call for a measurement.

But rather than hard-code a time delay into the process I would rather programmatically determine when the weight has stabilized.

This may be a solution. Perhaps I continuously weigh until the weight stops increasing, then do a final call.

Try something like that. Also, get to know the "normal" variation in measurements, when the weight rests on the balance. Consider that uncertainty in the code!

Well, I took a stab at it, but it's not doing what I expected. It's still registering light weights on the way up to stabilization, and it's still reporting overweights also on the way to stabilization.

Does anyone know if there is a HX711 library with a function that just tells you if the scale is stable and if so what the weight is?

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

/*
   Settling time (number of samples) and data filtering can be adjusted in the config.h file
   For calibration and storing the calibration value in eeprom, see example file "Calibration.ino"

   The update() function checks for new data and starts the next conversion. In order to acheive maximum effective
   sample rate, update() should be called at least as often as the HX711 sample rate; >10Hz@10SPS, >80Hz@80SPS.
   If you have other time consuming code running (i.e. a graphical LCD), consider calling update() from an interrupt routine,
   see example file "Read_1x_load_cell_interrupt_driven.ino".

   This is an example sketch on how to use this library
*/

#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;
float weight_grains = 0; // Variable for holding the weight in grains.
int Counter = 0; // Counter for items being weighed.
float CumulativeWeight = 0 ;// Cumulative weight of items being weighed.
float LastWeight = 0; // Variable for the weight of the previous item weighed.
const float StabFactor = .25 ; // The tolerance for determining if the weight has stabilized or not, in grains.
int NewScaleStatus = 0; // A flag to know whether the state of the scale has changed.
int OldScaleStatus = 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
  float calibrationValue; // calibration value (see example file "Calibration.ino")
  //calibrationValue = 696.0; // uncomment this if you want to set the calibration value in the sketch
#if defined(ESP8266)|| defined(ESP32)
  EEPROM.begin(512); // uncomment this if you use ESP8266/ESP32 and want to fetch the calibration value from eeprom
#endif
  EEPROM.get(calVal_eepromAdress, calibrationValue); // uncomment this if you want to fetch the calibration value from eeprom

  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()) {
    Serial.println("Timeout, check MCU>HX711 wiring and pin designations");
    while (1);
  }
  else {
    LoadCell.setCalFactor(calibrationValue); // set calibration value (float)
    Serial.println("Startup is complete");
  }
}

void loop() {
  LoadCell.setSamplesInUse(16);
  //  while (abs(CheckScale() - weight_grains) > StabFactor) {
  //    weight_grains = CheckScale();
  //    delay(5000);
  //    Serial.println("stabilizing1...");
  //  }

  weight_grains = CheckScale();
  if (weight_grains > 100) { //something is on the scale
    while (weight_grains > LastWeight) { // if the weight keeps going up, keep checking the scale
      LastWeight = weight_grains; // reset the last weight recorded.
      weight_grains = CheckScale(); // check the scale again.
      delay(500);
      Serial.println("Stabilizing...");
    }
  }

  if (weight_grains > 100) {
    if (weight_grains < 500) { // object is too light.
      NewScaleStatus = 1;
      if (NewScaleStatus != OldScaleStatus) {
        Serial.print("Weight is too light! ");
        Serial.println(weight_grains);
      }
      OldScaleStatus = NewScaleStatus;

    } else if (weight_grains > 600) { // object is too heavy .
      NewScaleStatus = 2;
      if (NewScaleStatus != OldScaleStatus) {
        Serial.print("Weight is to heavy !");
        Serial.println(weight_grains);
      }
      OldScaleStatus = NewScaleStatus;

    } else { // object is OK.
      NewScaleStatus = 3;
      if (NewScaleStatus != OldScaleStatus) {
        Serial.print("Weight is OK!");
        Serial.println(weight_grains);
      }
      OldScaleStatus = NewScaleStatus;
    }

  } else { // scale is empty.
    NewScaleStatus = 4;
    if (NewScaleStatus != OldScaleStatus) {
      Serial.println("Scale is empty.");
    }
    OldScaleStatus = NewScaleStatus;
  }

  // receive command from serial terminal, send 't' to initiate tare operation:
  if (Serial.available() > 0) {
    char inByte = Serial.read();
    if (inByte == 't') LoadCell.tareNoDelay();
  }

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

}

float CheckScale() {
  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();
      newDataReady = 0;
      t = millis();
      weight_grains = (i * 15.4324);
      //Serial.print("Load_cell output val: ");
      //Serial.println(weight_grains);
    }
  }
  return weight_grains;
}

Not really knowing but answering: No. That's software duty to handle.

For that to work, everyone would have to agree on a single definition of "stable". Of course, that is impossible.

You must fix that first.
Add (solder) a wire between E- and the HX711 ground pin.
Leo..

You must fix that first.
Add (solder) a wire between E- and the HX711 ground pin.

From what I am reading, this is not necessarily required. Is there something in my output that makes you think it is? I am getting valid results from the scale. My problem is not reading the scale, it is figuring out when the weight has stabilized.

I can't see why any soldering is required.

The load cell presents itself as a Wheatstone bridge that receives a voltage on the E-/E+ terminals and any variation at the A+/A- terminals is measured by the ADC in the HX-711. No grounding is required here. No continuity between E- and ground is expected.

Just make sure the resistance between any two of the load cell cables is either 1KΩ or 750Ω when disconnected from the HX-711

The HX-711 will measure voltage variations on A+/A- in the millivolt range. This is very susceptible to electrical interference. Most of the time variations are a result of electrical noise.

Try to isolate the cables that connect the load cell to the rest of the circuit. You can twist them and also put some shielding insulation (such as aluminum foil). For instance look at https://electricalbaba.com/how-twisting-the-cables-reduce-the-interference/.

Standard Ethernet twisted-pair cable can also help.

Should I twist all 4 wires together or should they be in pairs? If pairs, which 2 should be paired?

I have twisted black+red and white+green in pairs.

Try moving the cell (and so its cables) away from the rest of the circuit.

As recommended above, filtering capacitors also help.

I'm afraid I don't know anything about electronics so modifying the circuitry is beyond what I am able to do.

I tried twisting all 4 wires together and covering them with aluminum foil. I had been trying a different HX711 library and could not get it to work so I have come back to the original library, and now I'm getting bizzare results on calibration. So it's all borked up now.

OK, I have twisted the red and black together, and the white and green together.

Calibration routine is running again with good values. Not sure what made it start working, but this is what the output typically looks like in Serial Monitor:

Weight is stable to within .01 grams. But as you can see it does jump around, reading about 10-15times as 50.00 and then a dozen or so times as 50.01. One time it read as 49.99 in this capture.

I'm guessing this is as good as it gets.

What I really need is some help with the code to determine when the weight is stable.

Here is more output with a prototype sketch with the weight converted to grains.

I'm not getting a good zero, but I can tell when something is on the scale (anything greater than 100 and I assume something is on the scale).

The actual weight from an RCBS Chargemaster Lite scale is 556.4 grains, so its pretty close.

I think I'm going to try:

Detect something on scale greater than 100 grains
delay(5000)
Read the scale 10 times and take the average

And see what that gets me.

I don't like hardcoding the delay when it feels like I should be able to programatically figure out the stable value but I can't see my way to the algorithm to do it.