🔗Goal
Build a digital kitchen scale that weighs items using a load cell and HX711 amplifier, displays the weight on an OLED screen, and supports a tare button to zero out the container weight. This is a practical project that teaches you about analog-to-digital conversion, signal amplification, and calibration.
Here is how the system works at a high level:
graph LR
A[Load Cell] -->|Strain signal| B[HX711 Amplifier]
B -->|Digital data| C[ESP32]
C -->|I2C| D[OLED Display]
E[Tare Button] -->|GPIO| CA load cell is a transducer that converts force (weight) into a tiny electrical signal — typically just a few millivolts. The HX711 is a 24-bit analog-to-digital converter designed specifically for load cells. It amplifies the signal and sends it to the ESP32 as a digital value. The ESP32 then converts this raw value into grams using a calibration factor and displays the result on the OLED.
The relationship between the raw reading and weight is linear:
$$\text{weight (g)} = \frac{\text{raw reading} - \text{tare offset}}{\text{calibration factor}}$$
🔗Prerequisites
You will need the following components:
| Component | Qty | Notes | Buy |
|---|---|---|---|
| ESP32 dev board | 1 | AliExpress | Amazon.de .co.uk .com | |
| HX711 load cell amplifier module | 1 | AliExpress | Amazon.de .co.uk .com | |
| Load cell | 1 | 1 kg, 5 kg, or 10 kg — choose based on what you plan to weigh | AliExpress | Amazon.de .co.uk .com |
| SSD1306 OLED display (0.96") | 1 | I2C interface | AliExpress | Amazon.de .co.uk .com |
| Push button (tactile) | 1 | Momentary, normally open | AliExpress | Amazon.de .co.uk .com |
| 10k ohm resistor | 1 | Pull-down resistor for the button | AliExpress | Amazon.de .co.uk .com |
| Breadboard | 1 | AliExpress | Amazon.de .co.uk .com | |
| Jumper wires | ~12 | Male-to-male and male-to-female | AliExpress | Amazon.de .co.uk .com |
Links marked Amazon/AliExpress are affiliate links. We may earn a small commission at no extra cost to you.
Load cells come in different maximum capacities. A 1 kg cell is good for coffee and spices, 5 kg covers most kitchen ingredients, and 10 kg handles heavier items like flour bags. Choose the one that fits your use case — smaller capacity means higher precision for lighter items.
Arduino IDE libraries (install via Library Manager):
| Library | Author | Purpose |
|---|---|---|
| HX711 | Bogdan Necula (bogde) | Driver for the HX711 amplifier |
| Adafruit SSD1306 | Adafruit | OLED display driver |
| Adafruit GFX Library | Adafruit | Graphics primitives (dependency) |
🔗Tutorial
🔗Step 1: Wiring the load cell to the HX711
Load cells have four wires. The colors may vary between manufacturers, but the typical mapping is:
| Load Cell Wire | HX711 Pin | Common Color |
|---|---|---|
| Excitation+ (E+) | E+ | Red |
| Excitation- (E-) | E- | Black |
| Signal+ (A+) | A+ | White or Green |
| Signal- (A-) | A- | Green or White |
If your load cell's colors do not match this table, check the datasheet that came with your specific load cell. Swapping A+ and A- will invert the readings (weight goes negative), which is easy to fix by swapping the two wires.
🔗Step 2: Wiring the HX711, OLED, and button to the ESP32
| Component | Pin | ESP32 Pin | Notes |
|---|---|---|---|
| HX711 | VCC | 3.3V | |
| HX711 | GND | GND | |
| HX711 | DT (Data) | GPIO 4 | Digital data output |
| HX711 | SCK (Clock) | GPIO 5 | Clock input |
| OLED | VCC | 3.3V | |
| OLED | GND | GND | |
| OLED | SDA | GPIO 21 | I2C data |
| OLED | SCL | GPIO 22 | I2C clock |
| Tare button | One leg | GPIO 14 | |
| Tare button | Other leg | 3.3V | |
| 10k resistor | GPIO 14 | GND | Pull-down: keeps GPIO 14 LOW when button is not pressed |
Pin labels and GPIO numbers vary between ESP32 boards. Always check your board's pinout diagram. GPIO 21 and GPIO 22 are the default I2C pins on most ESP32-WROOM-32 DevKits.
The button circuit works as follows: when the button is not pressed, the 10k pull-down resistor keeps GPIO 14 at LOW (0V). When pressed, GPIO 14 connects to 3.3V and reads HIGH.
🔗Step 3: Calibration — finding your scale factor
Before the scale can display accurate weights, you need to determine the calibration factor for your specific load cell. This is a one-time process.
Upload this calibration sketch first:
#include "HX711.h"
#define HX711_DT 4
#define HX711_SCK 5
HX711 scale;
void setup() {
Serial.begin(115200);
Serial.println("HX711 Calibration");
Serial.println("==================");
scale.begin(HX711_DT, HX711_SCK);
Serial.println("Remove all weight from the load cell.");
Serial.println("Waiting 3 seconds...");
delay(3000);
scale.tare(20); // Average 20 readings for the zero point
Serial.println("Tare complete.");
Serial.println();
Serial.println("Place a known weight on the load cell.");
Serial.println("Then type the weight in grams into the Serial Monitor and press Enter.");
Serial.println("(Example: type 100 for a 100g weight)");
}
void loop() {
if (scale.is_ready()) {
long rawValue = scale.get_value(10); // Average of 10 readings
Serial.print("Raw value: ");
Serial.println(rawValue);
}
// Read known weight from Serial input
if (Serial.available()) {
float knownWeight = Serial.parseFloat();
if (knownWeight > 0) {
long rawValue = scale.get_value(20);
float calibrationFactor = (float)rawValue / knownWeight;
Serial.println();
Serial.print("Known weight: ");
Serial.print(knownWeight, 1);
Serial.println(" g");
Serial.print("Raw value: ");
Serial.println(rawValue);
Serial.print(">>> Calibration factor: ");
Serial.println(calibrationFactor, 2);
Serial.println();
Serial.println("Copy this number into the main sketch as CALIBRATION_FACTOR.");
}
}
delay(500);
}Calibration steps:
- Upload the calibration sketch and open the Serial Monitor at 115200 baud.
- Make sure nothing is on the load cell. The sketch will auto-tare after 3 seconds.
- Place a known weight on the load cell (a kitchen item with a printed weight works, or use calibration weights).
- Note the
Raw valueshown in the Serial Monitor. - Type the known weight in grams into the Serial Monitor input field and press Enter.
- The sketch will calculate and print your calibration factor. Write this number down.
🔗Step 4: Upload the main code
Replace the CALIBRATION_FACTOR value with the number you found during calibration.
#include "HX711.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ---- Pin definitions ----
#define HX711_DT 4
#define HX711_SCK 5
#define TARE_BTN 14
// ---- OLED configuration ----
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // No reset pin
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ---- Calibration ----
// Replace this with the value from the calibration sketch.
const float CALIBRATION_FACTOR = 420.0;
// ---- Globals ----
HX711 scale;
float currentWeight = 0.0;
unsigned long lastReadTime = 0;
const unsigned long READ_INTERVAL = 200; // Read every 200ms
void displayWeight(float weight) {
display.clearDisplay();
// Title
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(30, 0);
display.print("Kitchen Scale");
// Divider line
display.drawLine(0, 12, 127, 12, SSD1306_WHITE);
// Weight value — large text
display.setTextSize(3);
// Right-align the weight value
String weightStr;
if (weight >= 0) {
weightStr = String(weight, 1);
} else {
weightStr = String(weight, 1);
}
// Calculate cursor position for right-alignment (before the "g" label)
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(weightStr, 0, 0, &x1, &y1, &w, &h);
int xPos = 90 - w; // Leave room for "g" on the right
if (xPos < 0) xPos = 0;
display.setCursor(xPos, 22);
display.print(weightStr);
// Unit label
display.setTextSize(2);
display.setCursor(95, 26);
display.print("g");
// Bottom instruction
display.setTextSize(1);
display.setCursor(20, 54);
display.print("Press btn to tare");
display.display();
}
void setup() {
Serial.begin(115200);
// Initialize button
pinMode(TARE_BTN, INPUT);
// Initialize OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("ERROR: SSD1306 OLED not found.");
while (1) { delay(1000); }
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(20, 28);
display.print("Initializing...");
display.display();
// Initialize HX711
scale.begin(HX711_DT, HX711_SCK);
scale.set_scale(CALIBRATION_FACTOR);
scale.tare(20); // Average 20 readings for zero point
Serial.println("Kitchen Scale ready");
Serial.print("Calibration factor: ");
Serial.println(CALIBRATION_FACTOR, 2);
}
void loop() {
// Handle tare button
if (digitalRead(TARE_BTN) == HIGH) {
Serial.println("Tare button pressed");
display.clearDisplay();
display.setTextSize(1);
display.setCursor(30, 28);
display.print("Taring...");
display.display();
scale.tare(20);
delay(500); // Brief pause so the user sees the message
}
// Read weight at interval
if (millis() - lastReadTime >= READ_INTERVAL) {
lastReadTime = millis();
if (scale.is_ready()) {
currentWeight = scale.get_units(5); // Average of 5 readings
// Filter out tiny noise around zero
if (abs(currentWeight) < 0.5) {
currentWeight = 0.0;
}
displayWeight(currentWeight);
Serial.print("Weight: ");
Serial.print(currentWeight, 1);
Serial.println(" g");
}
}
}🔗Step 5: Using the scale
- Upload the main sketch and let it initialize. The OLED should show "Initializing..." briefly, then display
0.0 g. - Place a container (bowl, plate) on the load cell and press the tare button to zero it out.
- Add your ingredient. The weight updates in real time on the OLED screen.
- Press the tare button again any time you want to reset to zero (for example, between ingredients).
For the most stable readings, place the load cell on a flat, hard surface. Soft surfaces like tablecloths or cutting boards can introduce vibrations and inaccurate readings.
🔗Mounting the load cell
Most bar-type load cells need to be mounted with one end fixed and the other end free to deflect. A common setup:
- Screw one end of the load cell to a rigid base (a piece of wood or 3D-printed plate).
- Attach a flat platform (another piece of wood or acrylic) to the other end.
- The platform is where you place items to weigh.
Check your load cell's datasheet for the correct mounting orientation — the arrow printed on the cell typically indicates the direction of applied force.
🔗Common Issues and Solutions
| Problem | Cause | Fix |
|---|---|---|
| Readings drift slowly over time | Temperature changes affecting the load cell, or unstable mounting | Let the scale warm up for a few minutes. Tare before each use. Ensure the load cell is rigidly mounted |
| Weight reads negative | Load cell wires A+ and A- swapped | Swap the white and green wires on the HX711, or multiply the calibration factor by -1 |
| OLED shows "ERROR: SSD1306 OLED not found" | Wrong I2C address or wiring issue | Check SDA→GPIO 21, SCL→GPIO 22. Try I2C address 0x3D instead of 0x3C (some OLEDs use a different address) |
| Display shows weight but it is wrong | Calibration factor incorrect | Re-run the calibration sketch with a known weight. Double-check that you entered the weight in grams |
| Readings jump around wildly | Electrical noise, loose wiring, or unstable surface | Check all connections. Use shorter wires. Place the load cell on a solid, flat surface. Increase the averaging count in get_units() |
| HX711 never becomes ready | DT and SCK pins swapped, or module not powered | Verify DT→GPIO 4, SCK→GPIO 5. Check 3.3V power to the HX711 |
| Tare button does not respond | Pull-down resistor missing or wrong GPIO | Ensure 10k resistor connects GPIO 14 to GND. Check that the button connects GPIO 14 to 3.3V when pressed |
| Scale cannot measure small amounts accurately | Load cell capacity too large for the weight | A 10 kg cell has lower resolution for sub-gram measurements. Use a 1 kg cell for precision with small items |