Constant input to A0 pin from HX711 amplifier and load cell

Hello,

I am working on a project regarding tension control, however, this issue seems to possibly be a misunderstanding of sensors.

In this setup, tension is measured by using a dancer to apply force to a load cell (FSG15N1A).
As far as I understand, the HX711 amplifier uses the DAT pin and the CLK pin primarily for transmitting data. The CLK pin tells the DAT pin when to go to "low" and transmit data. We are clearly able to see values using the pins and code that have been developed for the HX711 amplifier and have calibrated it well.

However, we are not able to see a variable input from the amplifier when putting the Arduino pins for HX711 measurement and a PID input in series on a breadboard.

When they are in series, the A0 PID input reads 790 constantly, but the output adjusts to applied loads.
If I simply hook up the DAT pin to the A0 PID input, it reads 676.

I am very confused as to why the PID loop is clearly adjusting for tension in the output but we never read a change in input due to loading.

Wiring schematic attached. NOTE: The area labeled "series" connecting pin AO, 26, and DAT are truly in series on a breadboard unlike they are drawn.

//----------------- AMPLIFIER -----------------
  #include "HX711.h"

#define DOUT  26
#define CLK  24

HX711 scale(DOUT, CLK);

float calibration_factor = -69360; //-7050 worked for my 440lb max

//----------------- MOTOR DRIVER --------------

// Motor A


int in1 = 8;
int in2 = 7;

// Motor B


int in3 = 6;
int in4 = 5;




// ----------------------------PID-----------------
#include <PID_v1.h>

#define Constant 2

//Define Variables we'll be connecting to
double Setpoint, Input, Output;
int inputPin=0, outputPin=3;
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,0.7,0.5,.1,P_ON_M, DIRECT); //P_ON_M specifies that Proportional on Measurement be used
                                                            //P_ON_E (Proportional on Error) is the default behavior
unsigned long serialTime; //this will help us know when to talk with processing

void setup()

{
// -----------------AMPLIFIER -------------

  Serial.begin(9600);
  Serial.println("HX711 calibration sketch");
  Serial.println("Remove all weight from scale");
  Serial.println("After readings begin, place known weight on scale");
  Serial.println("Press + or a to increase calibration factor");
  Serial.println("Press - or z to decrease calibration factor");

  scale.set_scale();
  scale.tare();  //Reset the scale to 0

  long zero_factor = scale.read_average(); //Get a baseline reading
  Serial.print("Zero factor: "); //This can be used to remove the need to tare the scale. Useful in permanent scale projects.
  Serial.println(zero_factor);

// ----------------------PID ------------------

  {
  //initialize the variables we're linked to
  //initialize the serial link with processing
  Serial.begin(9600);
  Input = analogRead(inputPin);
  Setpoint = 815;

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
}

//-----------------MOTOR DRIVER -------------

  // Set all the motor control pins to outputs

 
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  
   
}

void loop() {

// -----------------AMPLIFIER --------------

scale.set_scale(calibration_factor); //Adjust to this calibration factor

  Serial.print("Reading: ");
  Serial.print(scale.get_units(), 3);
  Serial.print(" g"); 
  Serial.print(" calibration_factor: ");
  Serial.print(calibration_factor);
  Serial.println();
  
  if(Serial.available())
  {
    char temp = Serial.read();
    if(temp == '+' || temp == 'a')
      calibration_factor += 10;
    else if(temp == '-' || temp == 'z')
      calibration_factor -= 10;
  }

// -----------------PID ------------------------------

  {
  //pid-related code
  Input = analogRead(inputPin);
  myPID.Compute();
  analogWrite(outputPin,Output);
  analogWrite(Constant, 255);

  
  //send-receive with processing if it's time
  if(millis()>serialTime)
  {
    SerialReceive();
    SerialSend();
    serialTime+=500;
  }
  
  
}

  //MOTOR DRIVER ----------
  
  // Set Motor A forward

  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);

 // Set Motor B forward

  digitalWrite(in3, HIGH);
  digitalWrite(in4, LOW);
  analogWrite(Constant, 255);
}
/********************************************
 * Serial Communication functions / helpers
 ********************************************/


union {                // This Data structure lets
  byte asBytes[24];    // us take the byte array
  float asFloat[6];    // sent from processing and
}                      // easily convert it to a
foo;                   // float array



// getting float values from processing into the arduino
// was no small task.  the way this program does it is
// as follows:
//  * a float takes up 4 bytes.  in processing, convert
//    the array of floats we want to send, into an array
//    of bytes.
//  * send the bytes to the arduino
//  * use a data structure known as a union to convert
//    the array of bytes back into an array of floats

//  the bytes coming from the arduino follow the following
//  format:
//  0: 0=Manual, 1=Auto, else = ? error ?
//  1: 0=Direct, 1=Reverse, else = ? error ?
//  2-5: float setpoint
//  6-9: float input
//  10-13: float output  
//  14-17: float P_Param
//  18-21: float I_Param
//  22-245: float D_Param
void SerialReceive()
{

  // read the bytes sent from Processing
  int index=0;
  byte Auto_Man = -1;
  byte Direct_Reverse = -1;
  while(Serial.available()&&index<26)
  {
    if(index==0) Auto_Man = Serial.read();
    else if(index==1) Direct_Reverse = Serial.read();
    else foo.asBytes[index-2] = Serial.read();
    index++;
  } 
  
  // if the information we got was in the correct format, 
  // read it into the system
  if(index==26  && (Auto_Man==0 || Auto_Man==1)&& (Direct_Reverse==0 || Direct_Reverse==1))
  {
    Setpoint=double(foo.asFloat[0]);
    //Input=double(foo.asFloat[1]);       // * the user has the ability to send the 
                                          //   value of "Input"  in most cases (as 
                                          //   in this one) this is not needed.
    if(Auto_Man==0)                       // * only change the output if we are in 
    {                                     //   manual mode.  otherwise we'll get an
      Output=double(foo.asFloat[2]);      //   output blip, then the controller will 
    }                                     //   overwrite.
    
    double p, i, d;                       // * read in and set the controller tunings
    p = double(foo.asFloat[3]);           //
    i = double(foo.asFloat[4]);           //
    d = double(foo.asFloat[5]);           //
    myPID.SetTunings(p, i, d);            //
    
    if(Auto_Man==0) myPID.SetMode(MANUAL);// * set the controller mode
    else myPID.SetMode(AUTOMATIC);             //
    
    if(Direct_Reverse==0) myPID.SetControllerDirection(DIRECT);// * set the controller Direction
    else myPID.SetControllerDirection(REVERSE);          //
  }
  Serial.flush();                         // * clear any random data from the serial buffer
}


void SerialSend()
{
  Serial.print("PID ");
  Serial.print(Setpoint);   
  Serial.print(" ");
  Serial.print(Input);   
  Serial.print(" ");
  Serial.print(Output);   
  Serial.print(" ");
  Serial.print(myPID.GetKp());   
  Serial.print(" ");
  Serial.print(myPID.GetKi());   
  Serial.print(" ");
  Serial.print(myPID.GetKd());   
  Serial.print(" ");
  if(myPID.GetMode()==AUTOMATIC) Serial.print("Automatic");
  else Serial.print("Manual");  
  Serial.print(" ");
  if(myPID.GetDirection()==DIRECT) Serial.println("Direct");
  else Serial.println("Reverse");
}

The HX711 is a digital device. You don't read the output of the HX711 using the analog inputs, so remove the connection to A0.

It looks like you have mixed in some PID code intended for a completely different type of sensor.

Make sure that the HX711 and load cell is working correctly, and that you understand the output, before trying anything else. Sparkfun has a clear guide describing how to connect the HX711 to Arduino.

If you want to use the values produced by the HX711 as input for a PID controller, that is fine. However, those input values would be contained within a variable internal to your program.

Why is the VDD pin of the HX711 connected to 3.3volt.
That's if you use a 3.3volt Arduino.

A Mega is a 5volt Arduino, so both VCC and VDD go to the 5volt pin.
It shouldn't make a difference, but VDD to 5volt could make data more reliable.
Leo..

jremington:
The HX711 is a digital device. You don't read the output of the HX711 using the analog inputs, so remove the connection to A0.

It looks like you have mixed in some PID code intended for a completely different type of sensor.

Make sure that the HX711 and load cell is working correctly, and that you understand the output, before trying anything else.

Sparkfun has a clear guide describing how to connect the HX711 to Arduino.

If you want to use the values produced by the HX711 as input for some PID controller, that is fine. However, those input values would be internal to the program, not the hardware.

Hi, thank you for the reply.

I do understand that the HX711 is a digital device and I did actually use that guide! Our load cell and HX711 amplifier are working well and calibrated in gram-force down to 2 decimals.

And I tenuously understand how using PID code for analog input with a digital device would not work. I had been told by someone with more experience than me that this would work, and I had thought that the Arduino could convert those digital values into something readable in analog. (Very novice here).

Using the values produced by the HX711 for the PID was an option I had in mind.
i.e. Mapping the range of tension we see and using that as the PID input.

Do you know if it possible for me to use some digital to analog converter and continue as it is setup?

Wawa:
Why is the VDD pin of the HX711 connected to 3.3volt.
That's if you use a 3.3volt Arduino.

A Mega is a 5volt Arduino, so both VCC and VDD go to the 5volt pin.
It shouldn't make a difference, but VDD to 5volt could make data more reliable.
Leo..

I had just begun thinking about this the other day. I brainlessly followed the guide in this fashion without thinking about the fact that the board has a 5V logic level. I do not even think I have tried both VCC and VDD on the 5V pin.

I will try this, thank you!

And I tenuously understand how using PID code for analog input with a digital device would not work.

In your posted code, the PID algorithm is taking its input via the following line:

  Input = analogRead(inputPin);

Change that to something like (substitute the appropriate variable name):

  Input = value_obtained_from_HX711;

Be sure to carefully study the PID documentation for the allowable range of PID input values, PID output values, and how to set the Kp, Ki and Kd constants.

jremington:
In your posted code, the PID algorithm is taking its input via the following line:

  Input = analogRead(inputPin);

Change that to something like (substitute the appropriate variable name):

  Input = value_obtained_from_HX711;

Be sure to carefully study the PID documentation for the allowable range of PID input values, PID output values, and how to set the Kp, Ki and Kd constants.

Got it, i'll try this asap.

I have read through Brett Beauregard's PID example's, library, and blog post for improving the beginner's PID.

It is hard for me to analyze much of the code as it uses double so many times in ways I do not understand.
I do understand that he provided a way to clamp the output limits by defining outputMax and outputMin.

But is there a way for me to know what input values will be read? Or do I assume 0-1023, and map our live tension range with those values?

#ifndef PID_v1_h
#define PID_v1_h
#define LIBRARY_VERSION	1.2.1
;
class PID
{


  public:

  //Constants used in some of the functions below
  #define AUTOMATIC	1
  #define MANUAL	0
  #define DIRECT  0
  #define REVERSE  1
  #define P_ON_M 0
  #define P_ON_E 1

  //commonly used functions **************************************************************************
    PID(double*, double*, double*,        // * constructor.  links the PID to the Input, Output, and 
        double, double, double, int, int);//   Setpoint.  Initial tuning parameters are also set here.
                                          //   (overload for specifying proportional mode)

    PID(double*, double*, double*,        // * constructor.  links the PID to the Input, Output, and 
        double, double, double, int);     //   Setpoint.  Initial tuning parameters are also set here
	
    void SetMode(int Mode);               // * sets PID to either Manual (0) or Auto (non-0)

    bool Compute();                       // * performs the PID calculation.  it should be
                                          //   called every time loop() cycles. ON/OFF and
                                          //   calculation frequency can be set using SetMode
                                          //   SetSampleTime respectively

    void SetOutputLimits(double, double); // * clamps the output to a specific range. 0-255 by default, but
										                      //   it's likely the user will want to change this depending on
										                      //   the application
	


  //available but not commonly used functions ********************************************************
    void SetTunings(double, double,       // * While most users will set the tunings once in the 
                    double);         	    //   constructor, this function gives the user the option
                                          //   of changing tunings during runtime for Adaptive control
    void SetTunings(double, double,       // * overload for specifying proportional mode
                    double, int);         	  

	void SetControllerDirection(int);	  // * Sets the Direction, or "Action" of the controller. DIRECT
										  //   means the output will increase when error is positive. REVERSE
										  //   means the opposite.  it's very unlikely that this will be needed
										  //   once it is set in the constructor.
    void SetSampleTime(int);              // * sets the frequency, in Milliseconds, with which 
                                          //   the PID calculation is performed.  default is 100
										  
										  
										  
  //Display functions ****************************************************************
	double GetKp();						  // These functions query the pid for interal values.
	double GetKi();						  //  they were created mainly for the pid front-end,
	double GetKd();						  // where it's important to know what is actually 
	int GetMode();						  //  inside the PID.
	int GetDirection();					  //

  private:
	void Initialize();
	
	double dispKp;				// * we'll hold on to the tuning parameters in user-entered 
	double dispKi;				//   format for display purposes
	double dispKd;				//
    
	double kp;                  // * (P)roportional Tuning Parameter
    double ki;                  // * (I)ntegral Tuning Parameter
    double kd;                  // * (D)erivative Tuning Parameter

	int controllerDirection;
	int pOn;

    double *myInput;              // * Pointers to the Input, Output, and Setpoint variables
    double *myOutput;             //   This creates a hard link between the variables and the 
    double *mySetpoint;           //   PID, freeing the user from having to constantly tell us
                                  //   what these values are.  with pointers we'll just know.
			  
	unsigned long lastTime;
	double outputSum, lastInput;

	unsigned long SampleTime;
	double outMin, outMax;
	bool inAuto, pOnE;
};
#endif

But is there a way for me to know what input values will be read?

It is VERY IMPORTANT that you Serial.print(value_obtained_from_HX711) in your testing, so that you fully understand what the strain gauge/HX711 is reporting under all relevant operational circumstances.

Otherwise, you can't make sensible choices of Setpoint and the tuning constants.