1
\$\begingroup\$

I've been fighting with load cells for something like two weeks now. Lost two USB ports on my laptop to 12V in the process. Yet to hear of anyone having this much trouble. Looking for help.

Here are ~30 minutes worth of readings from a 100g load cell connected to a ADS1232 analog to digital converter module (tare done, no weight on the scale, using online dashboard InfluxDB to see data and store and process it): enter image description here

Huge drift/creep to the point that I wouldn't even call it creep/drift.

Here is my setup (lots of other stuff going on here, dw about it). enter image description here

Started with HX711 load cell ADC module. Then ADS1232 module. Added TLV1117 5V LDO with input and output filtering capacitors (10pF and 10µF for both) for excitation voltage. Nice, clean 5.005V supply voltage. I've checked with a multimeter. Tried with a 100g load cell (datasheet). 128x gain for range of +/-20mV. Tried with an expensive 300g load cell (datahsheet). 1x gain for range of +/-2.5V because output is 1.3V with no load (that's just how the Wheatstone bridge design is).

I've lost my mind.

I need to get to 10mg resolution. I'll be measuring samples' mass in a chemistry test--absorption of CO2 with passive air contacting. I read this scholarly article saying you can get 5mg accuracy with 100g TAL221 load cells available on eBay easy peezy. Imagine my frustration.

I read this article explaining how to perform temperature correction of load cells. That worked well with the 300g load cell. Its readings wouldn't wander, but then I got relatively little change with it when I applied a load, and variation in readings stayed bad. Way too inaccurate, basically.

(Here is link to ADS1232 library example program: link. Much of the calibration function can be ignored. Deleted everything and replaced with weight.OFFSET = weight.raw_read(20);

Here's my code:

#include "ADS1232.h" //https://github.com/ciorceri/ADS1232/tree/master
#include "BufferedOutput.h" //not using rn
#include <stdint.h>

#define _dout 16
#define _sclk 17
#define _pdwn 19

ADS1232 weight = ADS1232(_pdwn, _sclk, _dout);

void setup() {
  Serial.begin(115200);
  weight.power_up();
  delay(10*1000); //wait for 10 seconds
  weight.OFFSET = weight.raw_read(20); //tare
}

void loop() {
  if (weight.is_ready()) {
    Serial.println(weight.raw_read()); //it turns out weight.raw_read() spits out reading minus tare weight.
  }
  delay(500);
}

No idea what to do anymore. My teammates are looking at me like an idiot.

A huge thank you in advance to any helpful wizards out there.

Edit: At your guys' behest, I've gotten out my oscilloscope. Was shocked to see my excitation voltage is far from clean. I don't think this will solve the drift issue, but I think I'll solve it first, anyways. I need microvolt order ripple, not millivolt order ripple. Images with 100MHz bandwidth and 20MHz bandwidth, respectively:

enter image description here

enter image description here

I'll twist my voltage supply cables, and I'll twist the differential signal cables from the oscilloscope.

Edit #2: I've been asked to show schematic:

enter image description here

enter image description here

Another edit: Thank you to everyone for helping. I still haven't solved my problem, I'm afraid. I've implemented many of the suggestions I've received. I've created a whole new post with my updated situation: link. Thanks again.

\$\endgroup\$
17
  • 1
    \$\begingroup\$ Nice, clean 5.005V supply voltage. I've checked with a multimeter. ... how do you know that the power supply does not output a ripple? \$\endgroup\$
    – jsotola
    Commented Jul 19, 2023 at 16:53
  • 1
    \$\begingroup\$ It definitely does output a ripple. According to the datasheet, this will be 150µV, if I remember correctly. I only just got LDOs with 40µV ripple today, but I refuse to believe 150µV ripple is responsible for a constant, rapid drift like I'm seeing. \$\endgroup\$
    – Popeye
    Commented Jul 19, 2023 at 16:55
  • 1
    \$\begingroup\$ it looks like you may be repeatedly adding an offset \$\endgroup\$
    – jsotola
    Commented Jul 19, 2023 at 16:57
  • 1
    \$\begingroup\$ The wiring looks very.. Unshielded. Have you got any filters anywhere? It also does not help if you have a team with latops and cell phones radiating RF energy near the sensitive circuit. Please post schematics of everything. \$\endgroup\$
    – Justme
    Commented Jul 19, 2023 at 16:59
  • 2
    \$\begingroup\$ "Lost two USB ports on my laptop to 12V in the process" It's too late for that laptop, but in the future an inexpensive USB hub provides some protection. \$\endgroup\$
    – Ben Voigt
    Commented Jul 19, 2023 at 17:55

3 Answers 3

5
\$\begingroup\$

1x gain for range of +/-2.5V because output is 1.3V with no load (that's just how the Wheatstone bridge design is).

Not at all. The Wheatstone bridge has a differential output. That's what the ADC measures - that's why the ADC has differential inputs. You can use 128x gain with such a transducer. The only concern is the common mode range of the ADC you're using. As long as the ADC's common mode range includes 1.3V, the tiny differential voltage can be amplified by a lot before digitization.


  1. The load cell must connect nowhere but directly to the ADC pins.

  2. The load cell's aluminum body must connect directly to the same ground plane the ADC's AGND pin is connected to.

  3. A well-performing load cell can have some thermal drift in the first 10-30 minutes after power up, as the transducer reaches thermal equilibrium. This is mostly a concern in multi-axis systems where several strain gages, each dissipating about 1/4W of heat, are installed on a relatively thin section of the transducer. A single-axis transducer on a thick chunk of aluminum should not really suffer from that.

Nothing works very well unless those two rules are followed. The lack of body-to-GND connection is catastrophic most of the time, as it allows ESD to slowly destroy the strain gages and make them electrically leak to the transducer body.

For ratiometric measurements - absolutely critical for accuracy - the bridge must be powered from the same nodes the ADC takes reference voltage from. So, top and bottom of the bridge go to REFP and REFN. Of course you’ll supply REFP and REFN from somewhere, but what matters is that the load cell attaches to the ADC pins.

The other two conductors from the bridge go to AIN+ and AIN-.

And finally, the body of the load cell (the aluminum chunk) must be connected to the GND pin of the ADC.

Those are minimum requirements for any chance at it working well. There may be more that you can do but that’s how I do it an I’ve never had a problem. Modern ADCs have enough resolution to capture forces from breathing through the nose an inch away at the loadcell. Same loadcells you show in fact.

I have never had much success with breadboards for any of that. All the analog stuff, connectors included, needs to be on a PCB with a solid ground plane. The wires to the load cell must be twisted together.

Without the load cell aluminum body being connected to ADC GND, you'll get all sorts of interesting "results"

It's not impossible to get good results without following those rules, but it's much harder - and for a straightforward application like yours, it'd be an unnecessary complication.


If you asked me to do this measurement, I'd have put together a small PCB with the ADC, a wideband LDO for the ADC and excitation supply with significant rejection up to 1MHz, and some series resistors on the serial lines. That board would have a ground plane on the bottom and would be attached directly to the transducer, so that the ground plane contacts the transducer body through a small washer. Two mounting holes for #4 or smaller screws should be enough.

\$\endgroup\$
3
\$\begingroup\$

Between your schematic and the load-cell datasheet, the problem becomes clear.

I'm assuming you are trying to make the expensive loadcell work first... if that's not the case then this solution may not apply, because the different load cell models you have use different color schemes. In your photo it looks like you've wired up both load cells with the same color order, despite the different schemes....

For that load cell, the excitation power wires are green and black. These should connect to ADC REFP and REFN (and 5V and GND).

enter image description here

And red and white are the signal wires. These should connect to ADC AINP1 and AINN1.

But you have connected red and black to power and ground and tried to use green and white as signal.

enter image description here

Swap the loadcell's green and red wires.

\$\endgroup\$
1
  • \$\begingroup\$ This is crazy. Trying it as soon as I get back in the office on Monday. I realized that my 300g load cell's input and output resistances in the datasheet are measured across different wires than on my 100g load cell, but I didn't realize...this. Feeling like a dunce. Curious what results I'll get. Thank you. \$\endgroup\$
    – Popeye
    Commented Jul 22, 2023 at 10:43
2
\$\begingroup\$

This might not fix your described problem, but it is a problem.
From the library funtion raw_read(byte times), you MUST pass in the variable times. There is no initialization or protection for this variable, so it will take on whatever random value is on the stack. This could cause sum to overflow. You should be getting a compiler warning about this.

long ADS1232::raw_read(byte times) {
long sum = 0;
for (byte i=0; i<times; i++) {
   sum += _raw_read();;
}
   return sum/times;
}

So your code should look like this to take 1 reading:

void loop() {
  if (weight.is_ready()) {
    Serial.println(weight.raw_read( 1 )); //it turns out weight.raw_read() spits out reading minus tare weight.
  }
  delay(500);
}
\$\endgroup\$
5
  • \$\begingroup\$ C++ wouldn't allow a call with the wrong number of parameters to compile, so there's either a default argument or an overload or something. \$\endgroup\$
    – Ben Voigt
    Commented Jul 19, 2023 at 17:44
  • \$\begingroup\$ @BenVoigt You could be right. It looks to be compiler dependent. MS lists it as a warning: warning C4700: uninitialized local variable. Also, there are no other functions in the class with that name, so no overrides there. \$\endgroup\$
    – Aaron
    Commented Jul 19, 2023 at 17:52
  • \$\begingroup\$ My analysis is crippled by the question not having any information about the platform on which this code is running. I'm guessing that Serial.println is a strong indication of Arduino? \$\endgroup\$
    – Ben Voigt
    Commented Jul 19, 2023 at 17:59
  • \$\begingroup\$ @BenVoigt this must be the case, yes. I have added a 1 in there for good form. \$\endgroup\$
    – Popeye
    Commented Jul 19, 2023 at 17:59
  • \$\begingroup\$ I'm using PlatfromIO with the Arduino platform. \$\endgroup\$
    – Popeye
    Commented Jul 19, 2023 at 18:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.