4x 4-wire strain gauge and hx711 to esp32 issues

Hi everyone,

I'm new to developing my own projects, and I'm currently building a smart cat toilet that uses 4x 4-wire stain gauges (20kg each) hooked up to an hx711, itself connected to an esp32. I'm essentially building this man's project, and following his instructions: https://makerworld.com/en/models/1343408-smartcat-toilet?from=search#profileId-1384441.

The wires are connected in the way he recommended (wires of all load cells of the same color twisted together), and in the way both him and the aliexpress manufacturer state (red e+, black e-, green a+, white a-).

Initially I soldered a 1000µF 50v capacitor to the gnd and vcc of the hx711 - the maker suggested a 100µf capacitor instead, so I did that. Later I removed the capacitor entirely. In all cases, though, my issue is the same:

no matter how much weight I put on the platform, the readings are very stable. Whether it is 0, 3kg, 5kg, 7.5kg, 10kg, or 20kg, the base readings are 3180-90, and these hardly change at all. In all cases -capacitor wise-, I measured the voltage between the vcc and gnd of the hx711, which is roughly 5.1v. Between e- and e+ it is 4.132v. Strangely, between a- and a+ it is roughly 0,7v, though in my idea, it should be roughly a few millivolts as the signal needs to be amplified. I checked whether these wires were separated, and resistance between a- and a+ is 500ohm, which suggests they are.

I asked the original maker of the project, and he suggested me to use a 100µf capacitor rather than a 1000µf, but no matter whether I use a capacitor or not, I don't seem to get any measurement whatsoever. I can see the beams 'bending' a bit, they are hooked up using screws so there is in fact a cantilever process going on. Any Idea what the issue could be? I can share code, pictures, and videos.

Help is greatly appreciated.

Please read this very related discussion.

The only difference might be the load cells used...

If you read the pinned post re 'How to get the most from the forum' then you would know about posting code and wiring diagrams, NOT the instructions, but what you actually wired.

I have built a scale with an HX711 and always wonder why people think using more than one strain gauge will help. Just use one and follow one of the examples. There are very specific instructions on how to calibrate the gauge. Hint, a reading of 10lbs might be a strain gauge reading of 4,285, it doesn't read out in pounds.

There are several sample sketches, I have attached a screen shot. They are most easily accessed from the ... menu beside the library name.

The maximum input voltage on any ESP GPIO pin is 3.3V, yet this guy has several 5V signals connected including the one from the HX711

You should never connect any signal greater than 3.3V to any ESP GPIO otherwise you risk damaging the ESP

A schematic of you set-up would be nice.

Dear Rob Tillaart, I have in fact read the post, and figured that as my load cells are different, perhaps I'm missing something. Mine are 4-wire load cells. Not 3 wire ones. I'm new to electronics and was simply following the maker world tutorial, as shown by his videos and in his text. I simply do not have all the knowledge to get from a 3 wire load strain to a 4 wire load strain. That is why I create a new post.

Dear Tesla, I know very well that the difference does not read in pounds. I have in fact calibrated the scale using code, and the problem is that no matter how many pounds (or kilos in my case) I place on the scale, there is no difference comapared to the noise in the system. So: values are always roughly 3160-3170. With 10kg, 20kg, 5kg. I do know in fact the raw counts have to be converted to actual measurements. But here is my issue: it doesn't seem to show raw counts. Perhaps there is something wrong with the wiring or the code. But as I actually read the hx711 sensor values I figured at least it turned on and I know the wires of the signal a+ and a- are separated, so I'm not sure why it happens.

According to the specs of the hx711, it should be powered between 2.6-5.5v. The esp32 can output 5v and 3.3v. I tried powering it with 3.3v and that did not work. 5v works fine. However, if you think it would help, I'll give it another shot. However... The thing is the following: the 5v is just to power the hx711 with e+ and e-, these are not actually the signal a- and a+. Those are in millivolts -at least they should be- and are amplified by the hx711. So I'm not sure how giving it 3.3v by e+ and e- would help. I'll try again, perhaps you are right. But I suspect it is not the issue here. Also, please have a look at the original maker world tutorial. It shows that the vcc of the hx711 should be 5v, and for him that works. So I guess that is actually not my issue.

and what I mean with 'it doesn't show raw counts': it really doesn't show appreciable differences. When I calibrate it and then put weights on the scale, the readings fluctuate from -10kg to +46kg, but it is all roughly around 3160-3170. So I DO have an output signal. Just not a stable one. According to the original maker of the project, it is important to place a capacitor between the vcc and gnd of the hx711, exactly for that purpose. But I seem to have these issues regardless of what/or whether I use a capacitor. And besides that, the readings are in fact stable. They don't change. THAT is my issue.

#include <WiFi.h>
#include <ArduinoOTA.h>
#include <HX711.h>
#include <EEPROM.h>

// HX711 circuit wiring
const int LOADCELL_DOUT_PIN = 4;  // Changed from 2 to 4
const int LOADCELL_SCK_PIN = 5;   // Changed from 3 to 5

// WiFi credentials
const char* ssid = "";
const char* password = "";

// OTA settings
const char* ota_hostname = "esp32-cat-scale";
const char* ota_password = "";

HX711 scale;

// Calibration values stored in EEPROM
float calibration_factor = 1.0;
long zero_factor = 0;
const int EEPROM_SIZE = 512;
const int CALIBRATION_ADDR = 0;
const int ZERO_ADDR = 4;

// Menu states
enum MenuState {
  MAIN_MENU,
  RAW_READING,
  CALIBRATION,
  WEIGHT_READING,
  WIFI_STATUS
};

MenuState currentState = MAIN_MENU;

void setup() {
  Serial.begin(115200);
  Serial.println("ESP32 HX711 Scale Test & Calibration");
  Serial.println("====================================");
  
  // Initialize EEPROM
  EEPROM.begin(EEPROM_SIZE);
  
  // Initialize HX711
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  
  // Debug: Test pin states
  Serial.println("HX711 Pin Diagnostics:");
  Serial.print("DOUT pin (GPIO ");
  Serial.print(LOADCELL_DOUT_PIN);
  Serial.print("): ");
  Serial.println(digitalRead(LOADCELL_DOUT_PIN));
  
  Serial.print("SCK pin (GPIO ");
  Serial.print(LOADCELL_SCK_PIN);
  Serial.print("): ");
  Serial.println(digitalRead(LOADCELL_SCK_PIN));
  
  // Test if we can control SCK
  digitalWrite(LOADCELL_SCK_PIN, HIGH);
  delay(1);
  digitalWrite(LOADCELL_SCK_PIN, LOW);
  Serial.println("SCK pin toggle test completed");
  
  delay(100); // Give HX711 time to settle
  
  // Load calibration from EEPROM
  loadCalibration();
  
  // Initialize WiFi
  setupWiFi();
  
  // Setup OTA
  setupOTA();
  
  // Check if HX711 is ready
  if (scale.is_ready()) {
    Serial.println("HX711 initialized successfully!");
    Serial.print("Raw reading: ");
    Serial.println(scale.read());
  } else {
    Serial.println("HX711 not found! Check wiring:");
    Serial.println("- DOUT to pin 2");
    Serial.println("- SCK to pin 3");
    Serial.println("- VCC to 3.3V or 5V");
    Serial.println("- GND to GND");
  }
  
  showMainMenu();
}

void loop() {
  // Handle OTA updates
  ArduinoOTA.handle();
  
  // Handle serial input
  if (Serial.available()) {
    String input = Serial.readStringUntil('\n');
    input.trim();
    handleInput(input);
  }
  
  // Continuous readings in certain states
  if (currentState == RAW_READING) {
    showRawReading();
    delay(500);
  } else if (currentState == WEIGHT_READING) {
    showWeightReading();
    delay(500);
  }
  
  delay(10);
}

void setupWiFi() {
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 20) {
    delay(500);
    Serial.print(".");
    attempts++;
  }
  
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println();
    Serial.print("WiFi connected! IP address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println();
    Serial.println("WiFi connection failed. OTA updates will not be available.");
  }
}

void setupOTA() {
  if (WiFi.status() != WL_CONNECTED) return;
  
  ArduinoOTA.setHostname(ota_hostname);
  ArduinoOTA.setPassword(ota_password);
  
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else {
      type = "filesystem";
    }
    Serial.println("Start updating " + type);
  });
  
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
  });
  
  ArduinoOTA.begin();
  Serial.println("OTA Ready");
}

void loadCalibration() {
  EEPROM.get(CALIBRATION_ADDR, calibration_factor);
  EEPROM.get(ZERO_ADDR, zero_factor);
  
  // Check if values are valid (not NaN or extreme values)
  if (isnan(calibration_factor) || calibration_factor == 0 || 
      abs(calibration_factor) > 100000) {
    calibration_factor = 1.0;
    Serial.println("Invalid calibration factor, using default: 1.0");
  }
  
  if (zero_factor == 0 || abs(zero_factor) > 16777215) { // 24-bit ADC max
    zero_factor = 0;
    Serial.println("Invalid zero factor, using default: 0");
  }
  
  Serial.print("Loaded calibration factor: ");
  Serial.println(calibration_factor);
  Serial.print("Loaded zero factor: ");
  Serial.println(zero_factor);
}

void saveCalibration() {
  EEPROM.put(CALIBRATION_ADDR, calibration_factor);
  EEPROM.put(ZERO_ADDR, zero_factor);
  EEPROM.commit();
  Serial.println("Calibration saved to EEPROM");
}

void showMainMenu() {
  currentState = MAIN_MENU;
  Serial.println("\n==== MAIN MENU ====");
  Serial.println("1. Raw HX711 readings");
  Serial.println("2. Calibrate scale");
  Serial.println("3. Weight readings (kg)");
  Serial.println("4. WiFi status");
  Serial.println("5. Reset calibration");
  Serial.println("6. Test connection");
  Serial.println("Enter choice (1-6):");
}

void handleInput(String input) {
  if (currentState == MAIN_MENU) {
    if (input == "1") {
      currentState = RAW_READING;
      Serial.println("Showing raw readings (press 'm' to return to menu):");
    } else if (input == "2") {
      startCalibration();
    } else if (input == "3") {
      currentState = WEIGHT_READING;
      Serial.println("Showing weight readings (press 'm' to return to menu):");
    } else if (input == "4") {
      showWiFiStatus();
    } else if (input == "5") {
      resetCalibration();
    } else if (input == "6") {
      testConnection();
    }
  } else if (input == "m" || input == "M") {
    showMainMenu();
  } else if (currentState == CALIBRATION) {
    handleCalibrationInput(input);
  }
}

void showRawReading() {
  if (scale.is_ready()) {
    long reading = scale.read();
    Serial.print("Raw: ");
    Serial.print(reading);
    Serial.print(" | Average (10): ");
    Serial.print(scale.read_average(10));
    Serial.print(" | Zero offset: ");
    Serial.println(reading - zero_factor);
  } else {
    Serial.println("Scale not ready!");
  }
}

void showWeightReading() {
  if (scale.is_ready()) {
    scale.set_scale(calibration_factor);
    scale.set_offset(zero_factor);
    
    float weight = scale.get_units(5);
    Serial.print("Weight: ");
    Serial.print(weight, 2);
    Serial.print(" kg | Raw: ");
    Serial.print(scale.read());
    Serial.print(" | Calibration factor: ");
    Serial.println(calibration_factor);
  } else {
    Serial.println("Scale not ready!");
  }
}

void startCalibration() {
  currentState = CALIBRATION;
  Serial.println("\n==== CALIBRATION MODE ====");
  Serial.println("Step 1: Remove all weight from scale");
  Serial.println("Press 't' to tare (zero) the scale");
  Serial.println("Press 'c' to calibrate with known weight");
  Serial.println("Press 'm' to return to main menu");
}

void handleCalibrationInput(String input) {
  if (input == "t" || input == "T") {
    Serial.println("Taring scale... Please wait");
    if (scale.is_ready()) {
      zero_factor = scale.read_average(20);
      Serial.print("Tare complete. Zero factor: ");
      Serial.println(zero_factor);
      saveCalibration();
      Serial.println("Now place a known weight and press 'c' to calibrate");
    } else {
      Serial.println("Scale not ready!");
    }
  } else if (input == "c" || input == "C") {
    Serial.println("Enter the known weight in kg (e.g., 5.0):");
    Serial.println("Waiting for weight input...");
    
    // Wait for weight input
    while (!Serial.available()) {
      ArduinoOTA.handle();
      delay(10);
    }
    
    String weightStr = Serial.readStringUntil('\n');
    weightStr.trim();
    float knownWeight = weightStr.toFloat();
    
    if (knownWeight > 0) {
      Serial.print("Calibrating with ");
      Serial.print(knownWeight);
      Serial.println(" kg...");
      
      if (scale.is_ready()) {
        long reading = scale.read_average(20);
        long raw_weight = reading - zero_factor;
        
        if (raw_weight != 0) {
          calibration_factor = (float)raw_weight / knownWeight;
          Serial.print("Calibration complete! Factor: ");
          Serial.println(calibration_factor);
          saveCalibration();
          
          // Test the calibration
          scale.set_scale(calibration_factor);
          scale.set_offset(zero_factor);
          float measured = scale.get_units(5);
          Serial.print("Test measurement: ");
          Serial.print(measured, 2);
          Serial.println(" kg");
        } else {
          Serial.println("Error: No weight detected or scale not tared properly");
        }
      } else {
        Serial.println("Scale not ready!");
      }
    } else {
      Serial.println("Invalid weight entered");
    }
  }
}

void showWiFiStatus() {
  Serial.println("\n==== WIFI STATUS ====");
  Serial.print("Status: ");
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("Connected");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
    Serial.print("Signal Strength: ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
    Serial.print("OTA Hostname: ");
    Serial.println(ota_hostname);
  } else {
    Serial.println("Disconnected");
    Serial.println("Attempting to reconnect...");
    setupWiFi();
  }
  Serial.println("Press 'm' to return to main menu");
}

void resetCalibration() {
  calibration_factor = 1.0;
  zero_factor = 0;
  saveCalibration();
  Serial.println("Calibration reset to defaults");
  Serial.println("Press 'm' to return to main menu");
}

void testConnection() {
  Serial.println("\n==== CONNECTION TEST ====");
  Serial.println("Testing HX711 connection...");
  
  if (scale.is_ready()) {
    Serial.println("✓ HX711 is responding");
    
    // Test multiple readings
    long readings[5];
    bool stable = true;
    
    for (int i = 0; i < 5; i++) {
      readings[i] = scale.read();
      Serial.print("Reading ");
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.println(readings[i]);
      delay(100);
    }
    
    // Check for stability (variation should be reasonable)
    long min_reading = readings[0];
    long max_reading = readings[0];
    for (int i = 1; i < 5; i++) {
      if (readings[i] < min_reading) min_reading = readings[i];
      if (readings[i] > max_reading) max_reading = readings[i];
    }
    
    long variation = max_reading - min_reading;
    Serial.print("Reading variation: ");
    Serial.println(variation);
    
    if (variation < 1000) {
      Serial.println("✓ Readings are stable");
    } else {
      Serial.println("⚠ Readings show high variation - check connections");
    }
    
    // Test your 50V 1F capacitor effectiveness
    Serial.println("\nCapacitor test: Taking rapid readings...");
    for (int i = 0; i < 10; i++) {
      Serial.print(scale.read());
      Serial.print(" ");
      delay(50);
    }
    Serial.println("\nIf readings are stable, your capacitor is working well!");
    
  } else {
    Serial.println("✗ HX711 not responding");
    Serial.println("Check connections:");
    Serial.println("- DOUT (Data) to GPIO 2");
    Serial.println("- SCK (Clock) to GPIO 3");
    Serial.println("- VCC to 3.3V or 5V");
    Serial.println("- GND to GND");
    Serial.println("- Strain gauge wiring");
  }
  
  Serial.println("Press 'm' to return to main menu");
}

pretty much the exact way I wired it to the esp32. The code I used -minus my wifi login credentials- is above.

Connecting 5V signals to a 3.3V processor is a typical newbie mistake. Damage to the ESP may or may not happen right away. All I can do is tell you what is right and what is wrong.

According to the original maker of the project, it is important to place a capacitor between the vcc and gnd of the hx711, exactly for that purpose.

Indiscriminately throwing in capacitors is a another typical newbie maneuver that most often does not solve any problems. He probably corrected some wiring or code errors at the same time he added the cap.

Can you make it work with the HX711 basic example?

Hey Jim-P, thanks for answering so quickly. Using Rob Tillaart's example HX_calibration.ino v0.6.1, I tested it with both 5v and 3.3v (esp32 can output both -even if the pins only do 3.3.v logic): with 5v:

Determine zero weight offset

OFFSET: 3182

place a weight on the loadcell

enter the weight in (whole) grams and press enter

WEIGHT: 20501

SCALE: 0.000029

use scale.set_offset(3182); and scale.set_scale(0.000029);

in the setup of your project

with 3.3v

OFFSET: 2785

place a weight on the loadcell

enter the weight in (whole) grams and press enter

WEIGHT: 20501

SCALE: -0.000661

use scale.set_offset(2785); and scale.set_scale(-0.000661);

in the setup of your project

—used calibration with half the weight at 3.3v—

CALIBRATION

remove all weight from the loadcell

and press enter

Determine zero weight offset

OFFSET: 2776

place a weight on the loadcell

enter the weight in (whole) grams and press enter

WEIGHT: 10110

SCALE: 0.000435

use scale.set_offset(2776); and scale.set_scale(0.000435);

in the setup of your project

Wrong library!
Use the one he used.
Try the basic example.

Please post the datasheets for the load cells you are using.



Hey Jim-P, I'm not sure what you mean with 'wrong library'. I use the same library as he -tesla post and rob tillaart library- does, and the same version, with one of the sketches that are in there.

thanks for your help so far though.

Try this example. This will show you that your scale is working or not. If you have not mis-wired or damaged your esp32 or scale this will work.

//
//    FILE: HX_kitchen_scale.ino
//  AUTHOR: Rob Tillaart
// PURPOSE: HX711 demo
//     URL: https://github.com/RobTillaart/HX711


#include "HX711.h"

HX711 scale;

//  adjust pins if needed
//  uint8_t dataPin = 6;
//  uint8_t clockPin = 7;
uint8_t dataPin  = 19;  //  for ESP32
uint8_t clockPin = 18;  //  for ESP32

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println(__FILE__);
  Serial.print("HX711_LIB_VERSION: ");
  Serial.println(HX711_LIB_VERSION);
  Serial.println();

  scale.begin(dataPin, clockPin);

  Serial.print("UNITS: ");
  Serial.println(scale.get_units(10));

  Serial.println("\nEmpty the scale, press a key to continue");
  while(!Serial.available());
  while(Serial.available()) Serial.read();

  scale.tare();
  Serial.print("UNITS: ");
  Serial.println(scale.get_units(10));


  Serial.println("\nPut 1000 gram in the scale, press a key to continue");
  while(!Serial.available());
  while(Serial.available()) Serial.read();

  scale.calibrate_scale(1000, 5);
  Serial.print("UNITS: ");
  Serial.println(scale.get_units(10));

  Serial.println("\nScale is calibrated, press a key to continue");
  //  Serial.println(scale.get_scale());
  //  Serial.println(scale.get_offset());
  while(!Serial.available());
  while(Serial.available()) Serial.read();
}


void loop()
{
  Serial.print("UNITS: ");
  Serial.println(scale.get_units(10));
  delay(250);
}


//  -- END OF FILE --


He uses the one from bodge. If you hope to duplicate his cat device I suggest you use the same.

Use the basic example