- v2.19.0
- Use with Raspberry Pi
- Requires lgpio
- Code tested inside virtual Raspberry Pi Zero/3/4 environments on GitHub
- Looking for a Python alternative? Try here.
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.
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.
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
#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;
}
#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;
}
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:
-
data pin: Raspberry Pi pin which connects to the HX711 chip's data pin.
-
clock pin: Raspberry Pi pin which connects to the HX711 chip's clock pin.
Example using GPIO pin 2 for data and GPIO pin 3 for clock.
pi@raspberrypi:~/hx711 $ bin/hx711calibration 2 3
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:
-
data pin: Raspberry Pi pin which connects to the HX711 chip's data pin.
-
clock pin: Raspberry Pi pin which connects to the HX711 chip's clock pin.
-
reference unit: load cell's reference unit. Find this value with the calibration program above, otherwise set it to 1.
-
offset: load cell's offset from zero. Find this value with the calibration program above, otherwise set it to 0.
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
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 )
-
dataPin: Raspberry Pi pin which connects to the HX711 chip's data pin (also referred to as DOUT).
-
clockPin: Raspberry Pi pin which connects to the HX711 chip's clock pin (also referred to as PD_SCK).
-
refUnit: load cell's reference unit. Find this value with the calibration program described below, otherwise set it to 1.
-
offset: load cell's offset from zero. Find this value with the calibration program described below, otherwise set it to 0.
-
rate: HX711 chip's data rate. Changing this does not alter the rate at which the HX711 chip outputs data, but it is used to determine the correct data settling time. Changing the data rate requires modification of the hardware. On Sparkfun's HX711 breakout board, there is a jumper on the bottom of the board labelled
RATE
. By default, the jumper is closed, which sets the data rate to 10Hz. Opening the jumper (by cutting between the solder pads with a blade, desoldering, etc...) sets the data rate to 80Hz. Also seeSJ2
in the schematic.
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
SimpleHX711
and AdvancedHX711
both inherit from the HX711
class and provides these additional functions.
-
bool isReady( )
. Returns true if the HX711 chip has data ready to be retrieved. -
void waitReady( )
. When called, waits for the HX711 chip to have data ready, then returns. -
void setStrictTiming( bool strict )
. The HX711 chip has specific timing requirements which if not adhered to may lead to corrupt data. If strict timing is enabled, anIntegrityException
will be thrown when data integrity cannot be guaranteed. However, given the unreliability of timing on a non-realtime OS (such as Raspbian on a Raspberry Pi), this in itself is unreliable and therefore disabled by default. Use at your own risk. -
bool isStrictTiming( )
. Returns true if strict timing is used. -
void useDelays( bool use )
. If true, very short delays will be used during the period during which bits are read from the HX711 chip. These delays conform to the datasheet's specifications. On a Raspberry Pi, using delays is not likely to be useful unless, for some reason, the CPU is too fast. For that reason, the default is not to use them. -
bool isUsingDelays( )
. Returns true if delays are in use. See above. -
void setFormat( Format bitFormat )
. Defines the format of bits when read from the HX711 chip. EitherFormat::MSB
(most significant bit first - the default) orFormat::LSB
(least significant bit first). -
Format getFormat( )
. Returns theFormat
currently being used. -
int getDataPin( )
. Returns the GPIO pin number connected to the HX711's data pin (DOUT). -
int getClockPin( )
. Returns the GPIO pin number connected to the HX711's clock pin (PD_SCK). -
Channel getChannel( )
. Returns theChannel
being used when reading from the HX711 chip. -
Gain getGain( )
. Returns theGain
being used when reading from the HX711 chip. -
void setConfig( Channel c = Channel::A, Gain g = Gain::GAIN_128 )
. Changes the channel and gain of the HX711 chip. Anstd::invalid_argument
will be thrown if the given channel and gain are incompatible. Channel A may be set to a gain of 64 or 128. Channel B may only use a gain of 32. Please see the datasheet for more information. -
Value readValue( )
. Reads a value from the HX711 chip. You should generally not use this method. If you do, you must check whether the HX711 has data ready to be read (see:.isReady( )
). -
void powerDown()
-
void powerUp()
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.
-
Mass::Unit getUnit()
andvoid setUnit( Mass::Unit unit )
. Gets and sets the unit the scale will return weights in. For example, if set toMass::Unit::KG
, the scale will output a weight in kilograms. The default unit is grams (Mass::Unit::G
). -
Value getReferenceUnit()
andvoid setReferenceUnit( Value refUnit )
. See calibration program. -
Value getOffset()
andvoid setOffset( Value offset )
. Offset from zero. See calibration program. -
double normalise( double v )
. Given a raw value from HX711, returns a "normalised" value adjusted according to the scale's reference unit and offset. -
std::vector<Value> getValues( std::size_t samples )
. Returns a vector ofsamples
number of rawValue
s from the HX711 chip. You should use this method if you want to deal with raw, numeric values which have not been adjusted for weighing functions. -
std::vector<Value> getValues( std::chrono::nanoseconds timeout )
. Returns a vector of rawValue
s obtained from the HX711 chip withintimeout
. You should use this method if you want to deal with raw, numeric values which have not been adjusted for weighing functions. -
double read( Options o = Options() )
. Returns a numeric value from the scale according to the givenOptions
. The returned value has not been adjusted with .normalise()
. You should use this method if you want to deal with a single numeric value which has not been adjusted for weighing functions. -
void zero( Options o = Options() )
. Zeros the scale. -
Mass weight( Options o = Options() )
. Returns the current weight on the scale according to the givenOptions
. -
Mass weight( std::size_t samples )
. Returns the current weight on the scale using the median value fromsamples
number of samples. -
Mass weight( std::chrono::nanoseconds timeout )
. Returns the current weight on the scale using the median value from all samples collected within thetimeout
period.
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
.
-
StrategyType::Samples
instructs the scale to collectOptions.samples (std::size_t)
number of samples. This is the default. -
StrategyType::Time
instructs the scale to collect as many samples as possible within the time periodOptions.timeout (std::chrono::nanoseconds)
. -
ReadType::Median
instructs the scale to use the median value from the collected samples. This is the default. -
ReadType::Average
instructs the scale to use the average value from the collected samples.
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.
-
bool isMinSaturated( )
. Returns true if theValue
is out of range at the low-end of the load-cell. -
bool isMaxSaturated( )
. Returns true if theValue
is out of range at the high-end of the load-cell. -
bool isSaturated( )
. Returns true if theValue
is out of range at either the low-end or high-end of the load-cell. You could use this - or the otheris*Saturated
functions - to show an error message (as commonly seen on bathroom scales). -
bool isValid( )
. Returns true if the underlying value (val_t
) read from the HX711 is outside the normal operating range of possible valid values.
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:
-
Unit getUnit()
andsetUnit( Unit u )
to find and change the unit of mass. -
Mass convertTo( Unit to )
to return a newMass
object with the givento
unit. -
std::string toString()
which returns a formatted string containing the value of theMass
object in the accompanying unit of mass, followed the unit name. eg. "1.03 kg". -
std::string toString( Unit u )
which performs the same function as above, except according to the givenUnit
u
. -
std::ostream& operator<<( std::ostream& os, Mass& m )
to send the output oftoString()
to theostream
. For example:
Mass m(1.03, Mass::Unit::KG);
std::cout << m; //1.03 kg
- Unary and binary operators are also supported.
The following Mass::Unit
s 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 |
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.
-
All HX711 library code exists within the
HX711
namespace -
After building and installing the library (see below), you can
#include <hx711/common.h>
to include everything -
The current version of the library is accessible through
Utility::getVersion()
. It returns aconst char*
in the following format:{major}
.{minor}
.{patch}
(eg.2.4.1
). -
sudo make uninstall
from the project directory to remove the library -
If you are looking for a version of this library which uses wiringPi rather than lgpio, v1.1 is available. However, given that wiringPi is deprecated, I have chosen to use lgpio going forward.
"I just want to get some raw numbers from the scale".
There are a few different methods for this.
-
getValues( std::size_t samples )
andgetValues( std::chrono::nanoseconds timeout )
are both accessible fromSimpleHX711
andAdvancedHX711
and return anstd::vector<Value>
containing raw, unadjusted values from the HX711 chip. You should use this. -
double read( Options o = Options() )
is accessible fromSimpleHX711
andAdvancedHX711
. The difference between.read()
and.getValues()
is that.read()
takes an optionalOptions
argument to filter and return a single value. For example, by finding the average or median. -
HX711::readValue
is essentially what.getValues()
uses. But callingreadValue()
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.