endail / hx711

Raspberry Pi HX711 C++ Library
MIT License
18 stars 10 forks source link
cpp hx711 lgpio load-cell loadcell raspberry-pi weight

Raspberry Pi HX711 C++ Library

Build on Raspberry Pi cppcheck

Sample Output from Test Code

See: src/SimpleHX711Test.cpp

hx711.gif

The .gif above illustrates the output of the test code where I applied pressure to the load cell. The HX711 chip was operating at 80Hz. However, note from the code that the value being used is the median of three samples from the sensor. Also note the automatic formatting of the floating point numbers.

Build and Install

lgpio is required to be installed. It may be available to you via apt:

sudo apt-get install -y liblgpio-dev

If it is not available, manually build and install it by following the instructions here.

pi@raspberrypi:~ $ git clone --depth=1 https://github.com/endail/hx711
pi@raspberrypi:~ $ cd hx711
pi@raspberrypi:~/hx711 $ make && sudo make install

You may need to run ldconfig at this point if you attempt to compile a program and libhx711 is not found.

Use

After writing your own code (eg. main.cpp), compile and link with the HX711 and lgpio libraries as follows:

pi@raspberrypi:~ $ g++ -Wall -o prog main.cpp -lhx711 -llgpio

Examples

SimpleHX711 Example

#include <iostream>
#include <hx711/common.h>

int main() {

  using namespace HX711;

  // create a SimpleHX711 object using GPIO pin 2 as the data pin,
  // GPIO pin 3 as the clock pin, -370 as the reference unit, and
  // -367471 as the offset
  SimpleHX711 hx(2, 3, -370, -367471);

  // set the scale to output weights in ounces
  hx.setUnit(Mass::Unit::OZ);

  // constantly output weights using the median of 35 samples
  for(;;) std::cout << hx.weight(35) << std::endl; //eg. 1.08 oz

  return 0;

}

AdvancedHX711 Example

#include <chrono>
#include <iostream>
#include <hx711/common.h>

int main() {

  using namespace HX711;
  using std::chrono::seconds;

  // create an AdvancedHX711 object using GPIO pin 2 as the data pin,
  // GPIO pin 3 as the clock pin, -370 as the reference unit, -367471
  // as the offset, and indicate that the chip is operating at 80Hz
  AdvancedHX711 hx(2, 3, -370, -367471, Rate::HZ_80);

  // constantly output weights using the median of all samples
  // obtained within 1 second
  for(;;) std::cout << hx.weight(seconds(1)) << std::endl; //eg. 0.03 g

  return 0;

}

Calibrate

make will create the executable bin/hx711calibration in the project directory. You can use this to calibrate your load cell and HX711 chip. Arguments are as follows:

Example using GPIO pin 2 for data and GPIO pin 3 for clock.

pi@raspberrypi:~/hx711 $ bin/hx711calibration 2 3

Test

make will create the executables bin/simplehx711test and bin/advancedhx711test in the project directory. You can use these programs to test your load cell and HX711 module. Arguments are as follows:

Example using GPIO pin 2 for data, GPIO pin 3 for clock, -377 as the reference unit, and -363712 as the offset:

pi@raspberrypi:~/hx711 $ bin/simplehx711test 2 3 -377 -363712

Same example, except running as root using sudo with the AdvancedHX711 to use real-time scheduling. More information about this is avaliable below.

pi@raspberrypi:~/hx711 $ sudo bin/advancedhx711test 2 3 -377 -363712

Documentation

Datasheet

Revision 2.0

Wiring and Pins

The Sparkfun website has a tutorial on how to connect a HX711 breakout board to a load cell and to a microcontroller such as an Arduino. When connecting to a Raspberry Pi, the only significant difference is to connect the breakout board's VCC pin to a Raspberry Pi 5v pin, and the VDD pin to a Raspberry Pi 3.3v pin. Be very careful not to confuse the two or you could damage your Raspberry Pi.

Unless otherwise stated, use GPIO pin numbering. You do not need to use the dedicated Raspberry Pi SPI or I2C pins. The HX711 is not an I2C device. Any pin capable of input and output may be used.


There are two relevant classes for interfacing with a HX711: SimpleHX711 and AdvancedHX711.

SimpleHX711( int dataPin, int clockPin, Value refUnit = 1, Value offset = 0, Rate rate = Rate::HZ_10 )

As the name implies, this is a simple interface to the HX711 chip. Its core operation is busy-waiting. It will continually check whether data is ready to be obtained from the HX711 chip. This is both its advantage and disadvantage. It is as fast as possible, but uses more of the CPU's time.


AdvancedHX711( int dataPin, int clockPin, Value refUnit = 1, Value offset = 0, Rate rate = Rate::HZ_10 )

Arguments are identical to SimpleHX711.

The AdvancedHX711 is an effort to minimise the time spent by the CPU checking whether data is ready to be obtained from the HX711 module while remaining as efficient as possible. Its core operation, in contrast to SimpleHX711, is through the use of a separate thread of execution to intermittently watch for and collect data when it is available.

Additionally, the thread watching for and collecting data will alter its own CPU scheduling priority accordingly if it has permission to. In practice, this means that if executed with sudo, the thread will run in "real-time". You will note that from running htop simultaneously with the advancedhx711test program there is an entry for the watching thread with its priority set to RT (real-time). For example:

pi@raspberrypi:~/hx711 $ sudo bin/advancedhx711test 2 3 -377 -363712
  CPU[|||||||||||||                     30.6%]   Tasks: 34, 10 thr; 1 running
  Mem[|||||||||||||||||            39.4M/478M]   Load average: 0.77 0.53 0.27
  Swp[                              0K/100.0M]   Uptime: 1 day, 01:55:01

  PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
 1851 root       RT   0 30908  3012  2784 S 22.4  0.6  0:01.06 bin/advancedhx711test 2 3 -377 -363712

HX711

SimpleHX711 and AdvancedHX711 both inherit from the HX711 class and provides these additional functions.


AbstractScale

SimpleHX711 and AdvancedHX711 also both inherit from the AbstractScale class. This is the interface between raw data values from the HX711 chip and the functionality of a scale.


Options

You will notice in the functions above there is an Options parameter. This determines how data is collected and interpreted according to a StrategyType and ReadType.


Value

The Value class represents a useful value from the HX711 with some handy functions. A Value instance encapsulates a val_t type, which is a raw value from the HX711 after having been converted from a two's complement. You can use a Value in the same way as an int type.


Mass

Mass is a self-contained class to easily convert between units of mass. A Mass object contains a value stored as a double and a Mass::Unit representing the unit of that value. Methods of the Mass class you may find particularly useful include:

Mass m(1.03, Mass::Unit::KG);
std::cout << m; //1.03 kg

The following Mass::Units are supported:

identifier description toString suffix
Mass::Unit::UG micrograms μg
Mass::Unit::MG milligrams mg
Mass::Unit::G grams g
Mass::Unit::KG kilograms kg
Mass::Unit::TON metric tons ton
Mass::Unit::IMP_TON imperial tons ton (IMP)
Mass::Unit::US_TON US tons ton (US)
Mass::Unit::ST stones st
Mass::Unit::LB pounds lb
Mass::Unit::OZ ounces oz

Noise

It is possible that the HX711 chip will return - or the code will read - an invalid value or "noise". I have opted not to filter these values in this library and instead leave them up to the individual developer on how best to go about doing so for their individual application.

With that said, if you are looking for a simple but effective method to filter momentary noise, I highly recommend taking the median of at least three samples from the sensor. The SimpleHX711Test.cpp code does this and can be seen in the .gif above.

Other Notes

FAQ


"I just want to get some raw numbers from the scale".

There are a few different methods for this.

  1. getValues( std::size_t samples ) and getValues( std::chrono::nanoseconds timeout ) are both accessible from SimpleHX711 and AdvancedHX711 and return an std::vector<Value> containing raw, unadjusted values from the HX711 chip. You should use this.

  2. double read( Options o = Options() ) is accessible from SimpleHX711 and AdvancedHX711. The difference between .read() and .getValues() is that .read() takes an optional Options argument to filter and return a single value. For example, by finding the average or median.

  3. HX711::readValue is essentially what .getValues() uses. But calling readValue() does not check whether the HX711 chip is ready for a value to be read. Using this on its own will produce unreliable results.


"What's the difference between SimpleHX711 and AdvancedHX711?"

AdvancedHX711 uses a separate thread of execution to watch for and collect values from the HX711 chip when they are ready. It aims to be as efficient as possible. I recommend using AdvancedHX711 when you are obtaining a large number of samples.


"I'm receiving a "no samples obtained" std::runtime_error exception. What's going on?"

This exception will be thrown when no samples could be obtained from the HX711 chip. If you are obtaining samples based on time, you need to extend the amount of time allowed.