There are plenty of ways to monitor changes in your weight. You can get a vague idea from the fit of your pants or the notch on your belt. But anyone who’s serious about getting or staying in shape must step on the scale to get the cold, hard truth. So, instead of buying one, why not build your own?
In this tutorial, you’ll learn how to make a digital weighing scale using a load cell, the HX711 amplifier module, and an Arduino. We’ll start with the basics—how the load cell works and how to calibrate it to get accurate readings. Then, we’ll walk through a simple example to measure the weight of different objects. After that, we’ll upgrade the project by adding an LCD display to show the readings and a button to tare the scale.
Let’s get started!
Load Cell Basics
A load cell is a device (or transducer, if you prefer the technical term) that converts pressure (force) into an electrical signal. There are several types of load cells, including strain gauge load cells (which are the most common), hydraulic load cells, pneumatic load cells, and piezoelectric and capacitive load cells.
In this tutorial, we’ll focus on strain gauge load cells.

As the name suggests, strain gauge load cells use strain gauges, so let’s first understand what a strain gauge is and how it works.
A strain gauge
A strain gauge is a small sensor made of a thin wire or foil arranged in a zigzag or grid pattern. These sensors are usually smaller than a postage stamp, and look something like this:

When force is applied to the object the strain gauge is attached to, the gauge stretches or compresses slightly. This deformation causes a tiny change in its electrical resistance. The change in resistance is proportional to the amount of force applied.

However, the change in resistance is extremely small. For example, a strain gauge that normally has a resistance of 120 ohms might only change by about 0.12 ohms when force is applied. This tiny change is too small for most devices to detect directly. Thus, in order to use the strain gauge as a practical instrument, we must measure extremely small changes in resistance with high accuracy.
To do this, we are going to need another device that can either accurately measure super small changes in resistance (which can be expensive) or a device that can take that very small change in resistance and convert the small resistance change into a larger, more easily measured signal. This is where a special circuit called a Wheatstone bridge comes in handy.
A Wheatstone Bridge
A Wheatstone bridge is an electrical circuit made up of four resistors arranged in a diamond shape with a known voltage (Vin) applied across the bridge like this:

When all four resistors have the same value (the bridge is balanced), the output voltage (Vout) of the bridge is zero. But if the value of even one resistor changes (the bridge is unbalanced), Vout will have a resulting change that can be measured and is governed by the following equation using Ohm’s law:

How are Strain Gauges Used in Load Cells?
In a strain gauge load cell, one or more of the resistors in the Wheatstone bridge are replaced with actual strain gauges.

In a typical bar-type load cell, these gauges are bonded (glued) to a metal bar. The bar is often shaped in a “Z” formation.

When force is applied to the bar, it bends slightly. This bending causes two of the strain gauges to stretch (tension) and the other two to be squeezed (compression).

Because of how the Wheatstone bridge works, this setup creates a stronger and clearer signal from the change in resistance, which makes the force measurement more accurate.
Load Cell Wiring
A typical load cell has four wires, two for providing power (excitation) and two for measuring the signal, each corresponding to a connection point on the internal Wheatstone bridge:

- Excitation+ (E+ or VCC): Usually RED – This is where you apply the positive voltage to power the bridge.
- Excitation- (E- or GND): Usually BLACK or YELLOW – This is the ground or negative voltage connection.
- Output+ (O+, S+, or A+): Usually GREEN or BLUE – This wire carries the positive output signal from the bridge.
- Output- (O-, S-, or A-): Usually WHITE – This wire carries the negative output signal from the bridge.
The HX711 Module
Earlier, we saw how a load cell uses strain gauges arranged in a Wheatstone bridge to produce a voltage that is directly related to the amount of force or weight being applied. But here’s the important part: even after using the Wheatstone bridge, the resulting voltage signal is still extremely small—usually only a few millivolts. Regular microcontrollers, like an Arduino, are not sensitive enough to read such low-voltage signals directly.
This is where the HX711 comes in handy.
The HX711 is a specialized electronic chip designed specifically for reading signals from load cells. Its main job is to amplify the tiny voltage signal produced by the Wheatstone bridge and then convert that amplified analog signal into digital data using its built-in 24-bit Analog-to-Digital Converter (ADC), so that a microcontroller can easily read and process the information.

The HX711 communicates with a microcontroller using a simple, two-wire serial interface: DATA (DOUT) and CLOCK (SCK). The microcontroller initiates a conversion by pulling the SCK pin low. The HX711 then calculates the weight and sends the 24-bit digital value on the DOUT pin, with each bit clocked out on a rising edge of the SCK pin.
The HX711 has two different input channels, Channel A and Channel B. That means you can connect two separate load cells to it, one to each channel. You can set the gain for Channel A to either 64 or 128, while channel B has a fixed gain of 32. In most load cell applications, channel A is used, and the gain is typically set to 128 for the most sensitivity.

The number of clock pulses sent on the SCK pin controls the channel and gain setting for the next measurement. For instance, 25 pulses select channel A with a gain of 128, 26 pulses select channel B with a gain of 32, and 27 pulses select channel A with a gain of 64. However, you don’t need to worry about managing these pulse counts manually; there are libraries available that handle all of this for you using simple commands.
The HX711 can operate with a wide range of voltages, from 2.6V to 5.5V, which makes it compatible with common microcontrollers like Arduino and Raspberry Pi. It also uses very little power. Normally, it consumes less than 1.5 mA, but in a special low-power mode, it uses only about 1 µA. This makes it a great choice for battery-powered projects.
HX711 Module Pinout
Let’s take a look at the pinout.

Load Cell Connection Pins
E+ pin provides positive excitation voltage to the load cell. It is usually connected to the red wire of the load cell.
E- pin provides negative excitation voltage (ground) to the load cell. It is usually connected to the black wire of the load cell.
A+ is the positive differential input for Channel A, which is the primary channel used for connecting a load cell. It is usually connected to the white wire of the load cell.
A- is the negative differential input for Channel A. It is usually connected to the green wire of the load cell. The HX711 measures the tiny voltage difference between A+ and A- to determine the weight.
B+ is the positive differential input for Channel B. This is a secondary channel that can be used for a different load cell or a separate sensor.
B- This is the negative differential input for Channel B.
Power and Communication Pins
GND is the common ground connection for the entire circuit, including the HX711 module and your microcontroller.
DT (Data) is the serial data output pin. It sends the 24-bit digital value from the HX711 to your microcontroller.
SCK (Clock) is the serial clock input pin. Your microcontroller sends clock pulses to the HX711 on this pin to read the data.
VCC is for the power supply for the HX711 module itself. It can accept a voltage between 2.6V and 5.5V.
Load Cell Setup
Before we dive into the hookup and example code, it’s important to first set up the load cell.
When using a bar-type load cell, you’ll typically mount it between two flat plates in a “Z” shape like shown in the image below. This allows the load cell to experience strain between its opposite ends when weight is applied.

You can build this setup yourself using simple materials, or you can purchase an HX711 kit online. These kits usually include everything you need: two round acrylic plates, a bar-type load cell, an HX711 module, and some spacers and screws. The kit is designed to be easy to assemble, so you can get everything set up and running within minutes.
Connecting the Load Cell to the HX711 Module and Arduino
Let’s start wiring everything together.
First, grab your load cell. Most standard bar-type load cells have wires in four colors: red, black, white, and green. These wires need to be connected to the appropriate pins on the HX711 module.
Begin by connecting the red wire from the load cell to the E+ pin on the HX711. Then, connect the black wire to the E- pin. After that, connect the white wire to the A- pin, and finally, connect the green wire to the A+ pin. Once you’ve made these four connections, you’re finished with the load cell-to-HX711 wiring.
Now, let’s connect the HX711 module to your Arduino. Start by connecting the VCC pin on the HX711 to the 5V pin on your Arduino and the GND pin to the GND pin on the Arduino. After that, connect the DT pin on the HX711 to Arduino pin 2. Lastly, connect the SCK pin on the HX711 to Arduino pin 3.
The image below shows how to connect everything.

You’re now done with all the wiring! You can move on to uploading the code to your Arduino and start reading values from your load cell.
Library Installation
There are several different libraries available for reading measurements from a load cell using the HX711. In this tutorial, we’ll use the HX711 library by Bogdan Necula. It is compatible with many microcontrollers, including the ESP32, ESP8266, STM32, and Arduino.
To install the library:
- First open your Arduino IDE program. Then click on the Library Manager icon on the left sidebar.
- Type “hx711” in the search box to filter your results.
- Look for the “HX711 Arduino library” library created by Bogdan Necula.
- Click the Install button to add it to your Arduino IDE.

Calibrating the Load Cell
Each load cell is slightly different, even if they look the same. That means before your load cell can give accurate weight readings, it needs to be calibrated using an object with a known weight. Calibration helps your Arduino know how the raw numbers from the sensor translate into real-world units like grams or kilograms.
- To get started, first prepare an object with a known weight. For example, you can use a small bar of soap that weighs exactly 50 grams.
- Upload the calibration code to your Arduino board.
#include "HX711.h" #define DT 2 #define CLK 3 HX711 scale; void setup() { Serial.begin(9600); Serial.println("HX711 calibration sketch"); scale.begin(DT, CLK); } void loop() { scale.set_scale(); Serial.println(""); Serial.println("Remove all weight from scale"); delay(5000); scale.tare(); Serial.println("Tare done!"); Serial.println("Place a known weight on the scale..."); delay(5000); long reading = scale.get_units(10); Serial.print("Reading: "); Serial.println(reading); delay(1000); } - Open the Serial Monitor in the Arduino IDE and make sure it’s set to a baud rate of 9600. Then, press the RESET button on your Arduino board.
- Follow the instructions that appear on the Serial Monitor. First, remove all objects from the load cell or scale. The system will automatically “tare” itself, meaning it will treat the empty scale as zero. Next, place the known weight (for example, the 50g soap bar) onto the scale and wait for the Serial Monitor to display a value.

- Calculate your calibration factor using the formula:For example, if the reading you see is 21512 and your known weight is 50 grams, the calibration factor would be:


- Write down this calibration factor; you’ll need it in the next code to get accurate weight measurements.
Because the output of the sensor is proportional to the force applied to the load cell, you can calibrate your scale using whatever unit makes the most sense for your project. We used grams, but you can use pounds, kilograms, or even something custom—like pieces of dog food.
Also, please note that your calibration factor may be very positive or very negative. It all depends on the setup of your scale system and the direction the sensors deflect from zero state.
Example 1 – Measuring Weight
Once you’ve calculated the calibration factor for your load cell setup, you’re ready to use it to weigh real objects.
To do this, copy the code provided into your Arduino IDE. Before uploading it to your Arduino board, make sure you update the calibration_factor in the code to match the value you got during the calibration step.
#include "HX711.h"
#define DT 2
#define CLK 3
#define calibration_factor 430.24 //This value is obtained using the Calibration sketch
HX711 scale;
void setup() {
Serial.begin(9600);
Serial.println("HX711 scale demo");
scale.begin(DT, CLK);
scale.set_scale(calibration_factor);
scale.tare(); //Assuming there is no weight on the scale at start up, reset the scale to 0
}
void loop() {
Serial.print("Reading: ");
Serial.print(scale.get_units(), 1);
Serial.print(" gm");
Serial.println();
delay(1000);
}After uploading the code, open the Serial Monitor in the Arduino IDE and set the baud rate to 9600.
Make sure the scale is completely empty at the beginning. This allows the code to automatically tare the scale. Once the readings begin, gently place your known weight (such as the 50g soap bar you used earlier) on the scale. Watch the readings on the Serial Monitor.

If the value displayed doesn’t match the known weight, you may need to repeat the calibration process to get a more accurate calibration factor.
Now that the setup is complete, you can try placing different objects on the scale and observe their weights in real time.
Code Explanation
We begin the sketch by including the HX711 library. This library gives us all the functions we need to work with the HX711 module.
#include "HX711.h"Then, we define the two pins on the Arduino that are connected to the DT (data) and CLK (clock) pins on the HX711 module.
#define DT 2
#define CLK 3Next, we define a variable called calibration_factor. This is the number you found during calibration, and it tells the Arduino how to convert the raw sensor values into readable weight units, like grams. Be sure to replace the placeholder in the code with your actual calibration factor.
#define calibration_factor 430.24After that, we create an instance of the HX711 class and name it scale.
HX711 scale;Inside the setup() function, we first begin serial communication so we can see the output on the Serial Monitor. Then, we initialize the scale by calling the begin() method and passing in the pin numbers. We use the set_scale() method to apply the calibration factor, so the sensor knows how to convert its readings. Right after that, we use the tare() method to reset the scale to zero, assuming there’s no weight on it when the program starts.
void setup() {
Serial.begin(9600);
Serial.println("HX711 scale demo");
scale.begin(DT, CLK);
scale.set_scale(calibration_factor);
scale.tare(); //Assuming there is no weight on the scale at start up, reset the scale to 0
}Once the setup is complete, the scale is ready to measure weight. In the loop() function, we continuously call the get_units() method to get weight readings from the scale. The result is displayed on the Serial Monitor once per second.
void loop() {
Serial.print("Reading: ");
Serial.print(scale.get_units(), 1);
Serial.print(" gm");
Serial.println();
delay(1000);
}Example 2 – Creating a Simple Digital Weighing Scale
In the previous example, we used the Serial Monitor to display weight readings. Now, let’s take it a step further by creating a simple digital weighing scale that shows the readings on an I2C LCD display and includes a pushbutton to tare the scale when needed.
If you haven’t worked with I2C LCD display before, you might want to check out this helpful tutorial:
Wiring
The wiring in this example is mostly the same as before, but with a few additions. This time, we’re adding an I2C LCD and a pushbutton.
Start by connecting the I2C LCD to the Arduino. Connect the VCC pin of the LCD to the 5V output on the Arduino, and the GND pin to the Arduino’s ground. Then, connect the SDA pin of the LCD to A4 on the Arduino and the SCL pin to A5. On Arduino boards with R3 layout, you may also find SDA and SCL labeled separately near the AREF pin—either location will work.
Next, connect the pushbutton. One leg of the pushbutton should go to GND, and the other leg should be connected to digital pin 4 on the Arduino.
This diagram shows you exactly how to connect everything:

Arduino Code
Here is the Arduino sketch.
#include "HX711.h"
#include <LiquidCrystal_I2C.h>
#define DT 2
#define CLK 3
#define buttonPin 4
#define calibration_factor 430.24 //This value is obtained using the Calibration sketch
LiquidCrystal_I2C lcd(0x3F, 16, 2);
HX711 scale;
float reading;
float lastReading = -999; // Initialize to a value that won't match first reading
unsigned long lastEvent = 0; // For button debouncing
bool lastButtonState = HIGH; // Previous button state (HIGH when using INPUT_PULLUP)
void setup() {
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);
lcd.init();
lcd.clear();
lcd.backlight();
// Display title
lcd.setCursor(0, 0);
lcd.print("Digital Scale");
// Initialize scale
scale.begin(DT, CLK);
scale.set_scale(calibration_factor);
scale.tare(); // Reset scale to 0 at startup
// Initial weight display
lcd.setCursor(0, 1);
lcd.print("Weight: 0g ");
}
void loop() {
// Read current button state
bool currentButtonState = digitalRead(buttonPin);
// Check for button press with debouncing
if (lastButtonState == HIGH && currentButtonState == LOW) {
// Button was just pressed (falling edge)
if (millis() - lastEvent > 50) { // 50ms debounce delay
Serial.println("Tare!");
scale.tare();
// Clear weight display and show "Taring..."
lcd.setCursor(0, 1);
lcd.print("Taring... ");
delay(500); // Brief pause to show tare message
lastReading = -999; // Force display update after tare
}
lastEvent = millis();
}
// Update last button state
lastButtonState = currentButtonState;
// Read weight
reading = round(scale.get_units());
// Only update display if reading has changed significantly (reduce flicker)
if (abs(reading - lastReading) > 0.01) { // 0.01g threshold
lcd.setCursor(0, 1);
lcd.print("Weight: ");
lcd.print(reading, 0); // Display with 2 decimal places
lcd.print("g "); // Extra spaces to clear previous longer values
// Also print to serial for debugging
Serial.print("Weight: ");
Serial.print(reading, 2);
Serial.println("g");
lastReading = reading;
}
delay(100); // Small delay to prevent overwhelming the display and serial
}Demonstration
Once you’ve uploaded the code to your Arduino, you’re ready to start using your digital scale. When you place an object on the load cell, the weight will appear directly on the I2C LCD screen. If you want to reset the scale to zero—like when you change containers or remove all weight—just press the pushbutton and the scale will tare itself.
To test it, I weighed my wrist watch on this digital scale and then checked the same watch using a regular kitchen scale. The results matched perfectly, which shows that this setup is both accurate and reliable.

Code Explanation
The code is similar to the previous example, but this time we’re adding support for an I2C LCD display and a pushbutton.
At the start of the sketch, we include two libraries: the HX711.h library, which lets us communicate with the HX711 module, and the LiquidCrystal_I2C.h library, which is used to control the I2C LCD screen.
#include "HX711.h"
#include <LiquidCrystal_I2C.h>We then define the pins for the HX711 module: DT is connected to digital pin 2 and CLK to pin 3, just like before. We then define buttonPin and assign it to digital pin 4, which is the pin connected to the pushbutton. Next, we set the calibration_factor, which should be the value you found during your calibration process.
#define DT 2
#define CLK 3
#define buttonPin 4
#define calibration_factor 430.24 //This value is obtained using the Calibration sketchAfter that, we create two important objects. The first is lcd, which is an instance of the LiquidCrystal_I2C class and will be used to show data on the LCD screen. The address 0x3F is a common I2C address for these displays, and 16, 2 means the display has 16 columns and 2 rows. The second object is scale, which is our HX711 instance.
LiquidCrystal_I2C lcd(0x3F, 16, 2);
HX711 scale;We also create a few variables to help us later. reading will store the current weight reading. lastReading is initialized with a value far from normal readings so it won’t match the first actual reading when we later compare values. We also set up lastEvent and lastButtonState to help with debouncing the button.
float reading;
float lastReading = -999; // Initialize to a value that won't match first reading
unsigned long lastEvent = 0; // For button debouncing
bool lastButtonState = HIGH; // Previous button state (HIGH when using INPUT_PULLUP)Inside the setup() function, we start serial communication at 9600 baud, so we can still see output in the Serial Monitor. We set the button pin as INPUT_PULLUP, which means the button is normally HIGH and goes LOW when pressed.
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);Next, we initialize the LCD screen using lcd.init(), clear anything on it, and turn on the backlight. We display a title “Digital Scale” on the first line to let the user know the scale is ready. Then, we initialize the HX711 scale using scale.begin(DT, CLK), set the calibration factor, and call scale.tare() to reset the scale to 0 at startup. On the second line of the LCD, we display “Weight: 0g” as the initial reading.
lcd.init();
lcd.clear();
lcd.backlight();
// Display title
lcd.setCursor(0, 0);
lcd.print("Digital Scale");
// Initialize scale
scale.begin(DT, CLK);
scale.set_scale(calibration_factor);
scale.tare(); // Reset scale to 0 at startup
// Initial weight display
lcd.setCursor(0, 1);
lcd.print("Weight: 0g ");In the loop() function, we first check the current state of the button. Then, we check if the button has just been pressed. If the button was pressed and more than 50 milliseconds have passed since the last press, we run the tare operation.
When taring, we print “Tare!” in the Serial Monitor for feedback and call scale.tare() to reset the scale reading to zero. On the LCD screen, we briefly show “Taring…” so the user knows what’s happening. We then force the display to refresh by resetting lastReading.
// Read current button state
bool currentButtonState = digitalRead(buttonPin);
// Check for button press with debouncing
if (lastButtonState == HIGH && currentButtonState == LOW) {
// Button was just pressed (falling edge)
if (millis() - lastEvent > 50) { // 50ms debounce delay
Serial.println("Tare!");
scale.tare();
// Clear weight display and show "Taring..."
lcd.setCursor(0, 1);
lcd.print("Taring... ");
delay(500); // Brief pause to show tare message
lastReading = -999; // Force display update after tare
}
lastEvent = millis();
}
// Update last button state
lastButtonState = currentButtonState;After the button logic, we read the current weight using scale.get_units() and round the result. To avoid flickering on the LCD, we only update the screen if the new reading is significantly different from the last one. If the weight has changed, we update the LCD display with the new value and also print it to the Serial Monitor.
// Read weight
reading = round(scale.get_units());
// Only update display if reading has changed significantly (reduce flicker)
if (abs(reading - lastReading) > 0.01) { // 0.01g threshold
lcd.setCursor(0, 1);
lcd.print("Weight: ");
lcd.print(reading, 0); // Display with 2 decimal places
lcd.print("g "); // Extra spaces to clear previous longer values
// Also print to serial for debugging
Serial.print("Weight: ");
Serial.print(reading, 2);
Serial.println("g");
lastReading = reading;
}We finish with a short delay to prevent the screen and Serial Monitor from updating too quickly.
delay(100); // Small delay to prevent overwhelming the display and serial

