Load cell attenuation?

Not sure how to best describe what is happening and ask how to fix it but here it goes.

The application is having having two hiking poles instrumented with load cells to measure the force through the poles. When I apply a load to both load cells (relatively equally) they seem to follow the same pattern up until a certain threshold where one of the load cells seems to not be able to measure any higher of a load. This should not be the case because they are rated for much higher than the load that I am applying in this instance. I have attached a graph of what I mean for reference. I have tried to change my code to read one load cell at a time and I still get the same results.

Any ideas?

#include <RTClib.h>
#include <SD.h>
#include <Wire.h>
#include <Button.h>
RTC_DS1307 RTC; // define the Real Time Clock object

#define ADDRESS_1       (0x48) //device i2c address
# define CONFIG_REG     (0x01)
#define CONV_REG        (0x00)

//config reg
//OS
#define OS              (0x8000)
//mux
#define MUX_0_1         (0x0000)
#define MUX_0_3         (0x1000)
#define MUX_1_3         (0x2000)
#define MUX_2_3         (0x3000)
#define MUX_0_GND       (0x4000)
#define MUX_1_GND       (0x5000)
#define MUX_2_GND       (0x6000)
#define MUX_3_GND       (0x7000)
//pga
#define PGA_2_3RD       (0x0000)
#define PGA_1           (0x0200)
#define PGA_2           (0x0400)
#define PGA_4           (0x0600)
#define PGA_8           (0x0800)
#define PGA_16          (0x0A00)
#define PGA_16_1        (0x0C00) //TBR
#define PGA_16_2        (0x0E00) //TBR
//mode
#define SINGLE_SHOT     (0x0100)
#define CONT            (0x0000)

//samples per second
#define DRD_8           (0X0000)
#define DRD_16          (0X0020)
#define DRD_32          (0X0040)
#define DRD_64          (0X0060)
#define DRD_128         (0X0080)
#define DRD_250         (0X00A0)
#define DRD_475         (0X00C0)
#define DRD_860         (0X00E0)


//SET UP YOUR ADC HERE
//#define SETUP (OS|PGA_16|SINGLE_SHOT|DRD_860)// see line 93!!!
#define SETUP (OS|PGA_16|SINGLE_SHOT|DRD_860)// see line 93!!!

float cL;
float cR;




Button button = Button(8,PULLDOWN);
#define greenLED 3
#define redLED 4

long previousMillis = 0;
long interval = 9; // interval in milliseconds (10ms => 100Hz)

#define ECHO_TO_SERIAL    0 // echo data to serial port (set to 0 to turn off which should increase speed)
#define WAIT_TO_START    0 // Wait for serial input in setup()


// for the data logging shield, we use digital pin 10 for the SD cs line
const int chipSelect = 10;

// the logging file
File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);

  // red LED indicates error
  digitalWrite(redLED, HIGH);

  while (1);
}



///////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup(){
  Serial.begin(115200);
  Wire.begin();
  
  writeRegister(OS | ADDRESS_1, CONFIG_REG, SETUP | MUX_0_1);
  writeRegister(OS | ADDRESS_1, CONFIG_REG, SETUP | MUX_2_3);
  //c = conversionFactor(SETUP); //* 4096. ;//   / 32768.;
 cL = conversionFactor(SETUP)*1.91732;
 cR = conversionFactor(SETUP)*2.02114;

  // use debugging LEDs
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);

  #if WAIT_TO_START
  Serial.println("Type any character to start");
  while (!Serial.available());
#endif //WAIT_TO_START

  // initialize the SD card
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    error("Card failed, or not present");
  }
  Serial.println("card initialized.");

  // create a new file
  char filename[] = "HIKING00.CSV";
  for (uint8_t i = 1; i < 100; i++) {
    filename[6] = i / 10 + '0';
    filename[7] = i % 10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
//      logfile = SD.open(filename, FILE_WRITE);
        logfile = SD.open(filename, (O_READ | O_WRITE | O_CREAT));
      break;  // leave the loop!
    }
  }

  if (! logfile) {
    error("couldnt create file");
  }

  Serial.print("Logging to: ");
  Serial.println(filename);

  // connect to RTC
 // Wire.begin();
  if (!RTC.begin()) {
    logfile.println("RTC failed");
#if ECHO_TO_SERIAL
    Serial.println("RTC failed");
#endif  //ECHO_TO_SERIAL
  }


  logfile.println("millis,leftpoleforce,rightpoleforce");
#if ECHO_TO_SERIAL
  Serial.println("millis,leftpoleforce,rightpoleforce");
#endif //ECHO_TO_SERIAL
 
}



byte pressCount = 0;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop(){

 if (button.uniquePress())
pressCount++;

switch(pressCount){
       
       case 1:{
        
 digitalWrite(greenLED, HIGH);
  unsigned long currentMillis = millis();
   if(currentMillis - previousMillis > interval) {
      previousMillis = currentMillis;

  // log milliseconds since starting
  uint32_t m = millis();
  logfile.print(m);           // milliseconds since start
  logfile.print(", ");
#if ECHO_TO_SERIAL
  Serial.print(m);         // milliseconds since start
  Serial.print(", ");
#endif

//  readChannel(ADDRESS_1, MUX_0_1)*c; 
//  readChannel(ADDRESS_1, MUX_2_3)*c; 

  logfile.print(readChannel(ADDRESS_1, MUX_0_1)*cL-1.564);
  logfile.print(", ");
  logfile.print(readChannel(ADDRESS_1, MUX_2_3)*cR+1.998);  
  logfile.println();
  
#if ECHO_TO_SERIAL
  Serial.print(readChannel(ADDRESS_1, MUX_0_1)*cL);
  Serial.print(", ");
  Serial.print(readChannel(ADDRESS_1, MUX_2_3)*cR);  
  Serial.println();
#endif //ECHO_TO_SERIAL

  digitalWrite(greenLED, LOW);
       } }    
 break;

 case 2:{
 
  logfile.flush();
  logfile.close();
 
  digitalWrite(greenLED, LOW);
  digitalWrite(redLED, HIGH);
    delay(250);
  digitalWrite(redLED, LOW);
    delay(250);
  digitalWrite(redLED, HIGH);
    delay(250);
  digitalWrite(redLED, LOW);
    delay(250);
  digitalWrite(redLED, HIGH);
    delay(250);
  digitalWrite(redLED, LOW);
      delay(250);
  digitalWrite(redLED, HIGH);
    delay(250);
  digitalWrite(redLED, LOW);
      delay(250);
  digitalWrite(redLED, HIGH);
    delay(250);
  digitalWrite(redLED, LOW);
   pressCount = 0;
 }
  break;

}
}

void writeRegister(uint8_t i2cAddress, uint8_t reg, uint16_t value) {  //static
  Wire.beginTransmission(i2cAddress);
  Wire.write((uint8_t)reg);
  Wire.write((uint8_t)(value >> 8));
  Wire.write((uint8_t)(value & 0xFF));
  Wire.endTransmission();
}
 uint16_t readRegister(uint8_t i2cAddress, uint8_t reg) { //static
  Wire.beginTransmission(i2cAddress);
  Wire.write(reg);
  Wire.endTransmission();
  Wire.requestFrom(i2cAddress, 2);
  return ((Wire.read() << 8) | Wire.read());
}

boolean conversionReady(uint8_t i2cAddress) {
  uint16_t r = readRegister(ADDRESS_1, CONFIG_REG);
  return (r & (1 << 15)) >> 15;

}

int readChannel(uint8_t i2cAddress, uint16_t channel) {

  writeRegister(i2cAddress, CONFIG_REG, SETUP | channel);
  while (!conversionReady(i2cAddress));//only for Single shot
  uint16_t r = readRegister(ADDRESS_1, CONV_REG);
  return r;
}

float conversionFactor(uint16_t confReg) {
  float PGA = (confReg & 0x0E00) >> 9 ;
  if (PGA == 0) {
    PGA = .75;
  } else {
    PGA = pow(2., ((confReg & 0x0E00) >> 9) - 1);
  }
  return 1. / PGA;
}

I can't tell what the problem is, it might be hardware. But I do have many remarks about the sketch.

  • Use the spaces and '{' and '}' always in the same way. There is a Auto text Format tool in the menu : Tools / Auto Format.
  • The previousMillis must be unsigned long.
  • When you set pin 10 as output, it will be LOW. That means it is active. Set it high and wait a millisecond.
  • No '{' and '}' are needed in a case. They are used with "case 1:" and "case 2", but you can remove them.
  • Please write the floating point numbers as normal numbers: "0.0" instead of "0"; "0.75" instead of ".75"; "2.0" instead of "2."; and so on.
  • It is not possible to check for "0" or "0.0" for a floating point variable. Perhaps the value zero is the only value for which this is valid, but a floating point has always calculation and rounding errors and it is not okay to test if it is an integer value.
  • Perhaps the conversionFactor makes the difference that drops the output. Could you print the raw register value as well ?

We would like to know which chip your use, and how your hardware is, what you use for power supply and so on.

As far as the sketch goes, it was adapted from someone else. They wrote it the way they did because the library for the adafruit ADS1115 chip limits the speed which is a problem for my application. I added the case part to interface with a button so that I can start and stop data collection by pushing the button.

I am using an Uno board with adafruit data logger shield. The ADS 1115 chip is powered by the Uno's 5v pin. Both load cells are also powered by the 5v pin. The Uno is being powered via USB at the moment but will eventually be powered via battery pack that is 8 AA's but I am not to that point yet.

I've attached the load cell spec sheet. Both load cells are this model rated to 100kg

Here are the raw register values

corrected for bias

ads1115.pdf (950 KB)

tension compression load cell FL25.pdf (73.2 KB)

Looking at the graphs for something digital or repeating is almost like looking for extraterrestrial life by looking at noise.

Does the Adafruit library have the same ? Is perhaps testing the ready bit glitchy ?

I would extend the readRegister() function to check for valid return. If that doesn't help, read the datasheet very careful, or wait a little more after the ready bit becomes active.

  int n = Wire.requestFrom(i2cAddress, 2);      // request 2 bytes.
  if( n != 2)            // are two bytes received ?
    error("requestFrom didn't receive 2 bytes");

  return ((Wire.read() << 8) | Wire.read());

I have tried to just read one load cell at a time and slow down the speed but it still seems to do the same thing. Why would one load cell behave so differently than the other?

How are you applying the load and why are certain they are both being loaded exactly the same amount?

Can you take them out of the poles and test them under more controlled conditions (so any possible undesirable interactions with the poles are eliminated)?

Or, swap them between the poles and see if you still get the same results.

I am also interrested in the hardware.
What is the excitation voltage, is it regulated (stable), and does the output of the bridge fall into the measuring range of the A/D.
2mV/volt, is not a lot for the ADS1115 to work with.
AFAIK the ADS1115 needs 250mV differential at full PGA gain for max A/D count .
Post a diagram.
Leo..

Wawa, I appreciate your input and that had been a little bit of a concern. But for now, let's assume the one load cell is working perfectly for my needs when interfaced with both. Why does the second load cell behave so differently past a certain load? Which happens even when only that load cell is being read.

Dave, the load cells are threaded as you may have noticed from the spec sheet. I basically have the handles of the hiking poles threaded onto the top of the load cell. I don't currently have the adjustable sections of the poles screwed onto the bottom half of the load cells. Although not an exact science I have been gripping them side by side and just applying a load manually. As you can see I applied a pretty even load between the two. However when I started to apply a greater load that was closer to my body weight (160 lbs), the one load cell started to behave differently. That is what I need to figure out.

Thanks for sticking with me guys, I am no expert in this area

Perhaps you were sent one 100 kg cell and one 60 kg cell.

Or maybe you accidentally damaged one by overloading it. The datasheet says 150% overload is the "safe" overload. If you "bounce" your weight on it, you could approach or exceed 150% overload.

Or maybe you accidentally damaged it by bending it or putting a side load on it. The cells are for axial load only. Unfortunately the datasheet doesn't say how much bending or side load is allowed.

So how would you know if something has happened to a load cell, unless you test it in a controlled manner with known loads?

If you can't or won't take the cells out of the poles and test them with loads known to be exactly equal (and equal to the rated load if possible), then I'd suggest replacing the "bad" load cell with a new one.

Also, to test whether your wiring and hardware might be introducing errors, you could disconnect the load cell wires from the rest of your hardware and swap them (seems unlikely, but it would be simple to do, so why not check?)

And, as usual, it might be helpful if you posted a schematic and photographs.

PS: since these load cells are for axial loads only, it doesn't seem like such a great idea to put them in the middle of a hiking pole, where damage due to bending seems very likely to occur.