olkal / HX711_ADC

Arduino library for the HX711 24-bit ADC for weight scales
MIT License
235 stars 124 forks source link

Understanding Settling and Conversion Time #98

Closed anthnt closed 2 years ago

anthnt commented 2 years ago

Hello,

Thank you for providing this library. I am using a SparkFun HX711 set to 80 Hz and a single point load cell to measure thrust from a cold gas thruster. While I believe the HX711 is not the best ADC for this due to its limited update rate, it has been very easy to implement because of libraries like yours.

I am trying to read values as fast as possible from the load cell. After setting the HX711 to 80 Hz, I am wondering what is best practice to maximize reading speed. In the Readme, it is warned that using the built in moving average will increase settling time, which I am guessing you mean the time for the HX711 to collect the defined amount of samples and average them. Also by conversion time I am guessing you mean the time for the HX711 to read one sample from the load cell.

I ran your test script and it showed that conversion time and settling time is 0.64 ms and 12 ms for 16 samples, and 0.56 ms and 0 ms for 1 samples. Does this mean that for 16 samples, the value read in from the HX711 to the Arduino will be theoretically delayed by at least 12.64 ms from the load actually being applied?

When I applied a step input to the cold gas thrusters and measured their input using 16 and then 1 samples, I noticed that the settling time of my cold gas thruster load curves changed from around 300 to 150 ms respectively. I am trying to separate the settling time of the HX711 from the settling time of my cold gas thrusters (once impulsed) as best as possible.

Shown in the image is the thrust curve for the same thruster, with the same input. (top is 1 sample size, bottom 16 sample size).

ThrusterSettlingTime

anthnt commented 2 years ago

While I dont expect anyone to look through my code, if you are interested, I am reading load cell data the same way as shown in the examples. I've tested to make sure that the extra code in the while loop does not delay the reading in such that I cant get 80 Hz from the HX711.

void actuate()
{
  unsigned long time_on = 0; //How long the solenoid valve has been on for
  unsigned long time_on_prev = 0;
  unsigned long tStarted = 0; // Records when thruster actuation actually started
  unsigned long tEnded = 0; // Records when thruster actuation actually ended
  unsigned int sample = 0; // Records transducer sample number
  // Variable used to determine whether data from load cell is ready to read in
  static boolean newDataReady = 0;
  // Variable to control how much data from load cell is recieved (keep at 0)
  const int serialPrintInterval = 0;

  Serial.println("CSVSTART"); //Indicates start of CSV data to Python script
  Serial.println("Sample,RawLoadReading,RawPressureReading,Time,AirOn,TimeStarted,TimeEnded"); // CSV Column headers

  boolean turnedOn = false;
  _resume = false;
  time_on_prev = millis(); // Reads Arduino clock time to determine when the reading loop has started

  while(time_on < (air_on + before_air + after_air)) 
  {

    // Turn solenoids on
    if (_resume == false && time_on > before_air) 
    {   
      // Actuates solenoids 1,2,3,4 (or any combination thereof) 
      // once the time to read before actuation has ended
      if (numSolnd == 1)
      {
        tStarted = millis();
        PORTD = B00100000; // 1 (Load cell solenoid)
      }
      else if (numSolnd == 2)
      {
        tStarted = millis();
        PORTD = B00110000;  // 1,2
      }
      else if (numSolnd == 3)
      {
        tStarted = millis();
        PORTD = B00111000; // 1,2,3
      }
      else if (numSolnd == 4) 
      {
        tStarted = millis();
        PORTD = B00111100; // 1,2,3,4 (all)
      }
      else
      {
        Serial.println("Invalid number of solenoids selected");
        Serial.println("Please restart");
        return;
      }
      turnedOn = true;
      _resume = true;
    }

    if (time_on > (before_air + air_on) && turnedOn == true)
    {
        PORTD = B00000000; //Turn off solenoids
        tEnded = millis(); //Record actual time the solenoids were turned off
        turnedOn = false;
    }

    if (LoadCell.update())
    {
      newDataReady = true; // Check if data is ready to be read from load cell
    }

    if (newDataReady) 
    {
      if (millis() > t + serialPrintInterval) 
      {
        float i = LoadCell.getData(); // Read data from load cell. By default, the moving average data set of 1 sample is used.
        float p = analogRead(pinPressure);
        sample = sample + 1;
        Serial.print(sample);
        Serial.print(",");
        Serial.print(i);
        Serial.print(",");
        Serial.print(p);
        Serial.print(",");
        t = millis();
        Serial.println(t);
        newDataReady = 0;
      }
    }
    else
    {
      // comment this block out to match frequency of pressure reading
      // to frequency of load cell reading
      float p = analogRead(pinPressure);
      sample = sample + 1;
      Serial.print(sample);
      Serial.print(",,");
      Serial.print(p);
      Serial.print(",");
      Serial.println(millis());
    }

    time_on = millis() - time_on_prev; // update time counter
  }
  // close all solenoids
  PORTD = B00000000; //Ensure solenoids are off

  Serial.print(",,,,");
  Serial.print(air_on);
  Serial.print(",");
  Serial.print(tStarted);
  Serial.print(",");
  Serial.println(tEnded);
  Serial.println("CSVEND"); //Tells the python script to stop reading
}
olkal commented 2 years ago

Hi!

You are correct; I have used the term "conversion time" for the interval from when one conversion from the HX711 is ready to read out until the next conversion is ready, and the term "settling time" for the time it takes to fill up the data set (number of SAMPLES + IGN_HIGH_SAMPLE + IGN_LOW_SAMPLE) so that the averaged value is representative for the applied load.

However, the numbers you get from the test sketch seems wrong, at HIGH rate I get the following numbers from the test.ino:

With default values in config.h file: HX711 measured conversion time ms: 10.67 HX711 measured sampling rate HZ: 93.70 HX711 measured settlingtime ms: 172

With config.h file values set to SAMPLES 1, IGN_HIGH_SAMPLE 0, IGN_LOW_SAMPLE 0: (with this config there is no averaging at all, hence settling time = conversion time) HX711 measured conversion time ms: 10.67 HX711 measured sampling rate HZ: 93.70 HX711 measured settlingtime ms: 10

The change you describe from around 300 to 150 ms seems about right.

Note that the HX711 internal clock is not accurate and will usually run a bit faster than given in the data sheet, so a sample rate of about 90HZ at HIGH rate is normal.

For reading as fast as possible, you will just have to experiment a bit and find the best compromise between speed and accuracy for your application, there is no magic setting. I know some people have used SAMPLES 4, IGN_HIGH_SAMPLE 0 and IGN_LOW_SAMPLE 0 for game controllers.

There is a few things you can consider though:

anthnt commented 2 years ago

Thank you for the explanation and the recommendations, this cleared up my confusion.