Need help optimizing hx711 code

I should preface this by saying I'm very inexperienced with coding in Arduino.

So the goal of my code is to have a servo open a container door and pour coffee beans into a bin, where a load cell reads the weight of the beans poured in and once a certain predetermined weight is reached the servo closes the container door. However, I've run into two main issues. First the sample speed of the loadcell and hx711 are too slow for this application, by the time the loadcell recognizes that the required weight has been met, the actual weight of the beans in the bin is twice that of the goal weight.

Secondly, the code I 'wrote' for this is mainly pulled from the hx711 library, and I don't understand some of it. The end goal of the project is for this to run with out being plugged into a computer, so using the serial window won't be possible, but the code currently won't run unless the serial window is open.

The program works as intended besides these two main points, so any pointers on how to fix these would be greatly appreciated!

#include <HX711_ADC.h>
#include <Servo.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
const int servoPin = 3; 

//Servo Stuff
int servoPosition = 0;
int servoInterval = 500;
int servoDegrees = 60;
int weight = 0;

unsigned long currentMillis = 0;
unsigned long previousServoMillis = 0;

// Create a servo object 
Servo Servo1; 

//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...");
  Servo1.write(0);
  Servo1.attach(servoPin); 

  LoadCell.begin();
  
  float calibrationValue; // calibration value (see example file "Calibration.ino")
  
#if defined(ESP8266)|| defined(ESP32)
 
#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() {

 
  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, 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 measurement = LoadCell.getData();
weight = measurement;

currentMillis = millis();

servoFunction();
}
void servoFunction() {
  
if (weight >= 20) {
  if (currentMillis - previousServoMillis >= servoInterval) {
   
    servoPosition = 0;
    
  Servo1.write(servoPosition);
  delay(2000);
  exit(2000);
  }
 }
 else {
  if (currentMillis - previousServoMillis >= servoInterval) {
   
    servoPosition = 60;
    
  Servo1.write(servoPosition);
  }
 }
}

How long does it take to read the weight?

The Arduino doesn't care if anything is listening on serial. Are you saying that you have to send something (tare command?) to make it work?

I don't have to type anything into the serial window, but the servo won't do the initial turn without the serial window being open. To be clear this initial turn is not dependent on anything, so it should happen immediately.

I haven't measured how long it takes to read the weight exactly, but I would estimate it takes it 1.5-2 seconds to read a 25g weight I use to calibrate it.

What is the Arduino and how is the servo powered. Is it different when Serial is not in use?

Its an arduino uno, and the servo is powered from the arduino. I don't think the servo is the core of the problem here, as it works as intended.

The program does not run if the serial window isn't open

I can't see why that would be. However, servo problems are usually power related. Powering them from an Arduino is risky business - they need their own supply.

I don't think any of this is a servo problem, the servo works as intended.

I will look into getting the servo its own supply, is it risky because of the current?

Yes - the Arduino can barely provide enough current to run a small servo with no load on it if it is powered through the USB port. If you are using the barrel jack, it's substantially worse.

what is it?

just ends the program for me so the door doesnt open again at the end

door? is it not weighing-machine? where you got such a program ending method?

as stated in the post, the servo opens a door

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

const byte HX711_dout = 4; //mcu > HX711 dout pin
const byte HX711_sck = 5; //mcu > HX711 sck pin
const byte servoPin = 3;

//Servo Stuff
const unsigned int servoInterval = 500;
int weight = 0;

unsigned long previousServoMillis = 0;

// Create a servo object
Servo Servo1;

//HX711 constructor:
HX711_ADC LoadCell(HX711_dout, HX711_sck);

const unsigned int calVal_eepromAdress = 0;

void setup() {

  Serial.begin(57600); delay(10);
  Serial.println();
  Serial.println("Starting...");
  Servo1.attach(servoPin);
  Servo1.write(0);
  LoadCell.begin();

  float calibrationValue; // calibration value (see example file "Calibration.ino")
  EEPROM.get(calVal_eepromAdress, calibrationValue); // uncomment this if you want to fetch the calibration value from eeprom

  LoadCell.start(2000, true);
  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() {
  static boolean newDataReady = false;
  if (LoadCell.update()) newDataReady = true;// check for new data/start next conversion:

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

  // 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() )Serial.println("Tare complete");

  if ( millis() - previousServoMillis >= servoInterval) {
    previousServoMillis += servoInterval;
    weight = LoadCell.getData();
    if (weight >= 20 ) {
      Servo1.write(0);
      while(1);
    }
    else Servo1.write(60);
  }
}

wait, why weight = LoadCell.getData(); must waiting 500ms if you say "measuring take too long for correct weight"? should i remove this brake?

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

const byte HX711_dout = 4; //mcu > HX711 dout pin
const byte HX711_sck = 5; //mcu > HX711 sck pin
const byte servoPin = 3;
const unsigned int servoInterval = 500;
unsigned long previousServoMillis = 0;
const unsigned int calVal_eepromAdress = 0;

Servo Servo1;
HX711_ADC LoadCell(HX711_dout, HX711_sck);

void setup() {
  Serial.begin(57600); delay(10);
  Serial.println();
  Serial.println("Starting...");
  Servo1.attach(servoPin);
  Servo1.write(0);
  LoadCell.begin();

  float calibrationValue; // calibration value (see example file "Calibration.ino")
  EEPROM.get(calVal_eepromAdress, calibrationValue); // uncomment this if you want to fetch the calibration value from eeprom

  LoadCell.start(2000, true);
  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");
  }

  LoadCell.tareNoDelay();
  while (!LoadCell.getTareStatus());
  Serial.println("Tare complete");
}

void loop() {
  static int weight = 0;
  if (LoadCell.update()) {
    weight = LoadCell.getData();
    if (weight >= 20 ) {
      Servo1.write(0);
      while (1);
    }
    else Servo1.write(60);
  }
}

is servo behavior inverted?

Sorry I dont really get what you're asking, as I said I am fairly inexperienced with arduino.

I've used 'weight = LoadCell.getData();' to declare a variable, which is used to tell the servo when to stop. Is this causing a 500ms wait time every iteration? if so how do I fix it?

No I dont believe the servo is inverted

post #14 is fixed.
last thing to do is rejecting serial communication.

The OP's HX711 library function, getData(), returns the result of a moving average. The default sample size is 16.

So at 10 sps, the HX711 default rate, when there is a sudden change in weight, it will take about 1.6 seconds for the moving average to stabilize.

In the OP's application, as the coffee beans flow out, the weight will be increasing gradually, so it is not clear whether the moving average would significantly affect the device's performance.

I'd experiment with a smaller moving average sample size, or not use it at all.

1 Like

This is great advice thank you!

My understanding of the library isn't too great, any suggestions on how to adjust the sample size or not using the moving average?

Thanks, but that remains to be seen. :slight_smile:
(The code @kolaha gave you in #14 is likely to be more helpful.)

My understanding of the library isn't all that great either. In fact, I've never used that one. So what I did was google the library name in your code: HX711_ADC.h

That took me to this site: GitHub - olkal/HX711_ADC: Arduino library for the HX711 24-bit ADC for weight scales

Presumably, that is the source of the library installed on your computer.

That website points out that a moving average is used. It also talks about the HX711 sampling rate.

The 2nd google hit was the .h file itself

(also in the "src" folder of the library on your computer) where, among other "public" functions (i.e., those available to the user, that's you, to use) is

void setSamplesInUse(int samples); //overide number of samples in use

So, it seems that if you want to change the number of moving average samples, then at the end of setup(), you should add:

setSamplesInUse(x);

where x = one of the valid sample sizes listed near the top of the .h file

PS: you could also use a different HX711 library, one that just returns a single (unaveraged) value with every call. But, since you're already down the path with this library, I guess you may as well stay with it.

1 Like