bogde / HX711

An Arduino library to interface the Avia Semiconductor HX711 24-Bit Analog-to-Digital Converter (ADC) for Weight Scales.
MIT License
874 stars 535 forks source link

HX711 Read returning different value with same load cell but on different MCU #61

Open mashhashkarma opened 7 years ago

mashhashkarma commented 7 years ago

hi Bogde, Thank you for your HX711 library code. I used it on HX711 and it worked well. I tried to used same on another MCU. I observed that the values returned by Read() on Arduino is significantly different from that on another board (NRF52832) though the HX711 module and the loadcell is the same. Yes, have used 3.3v in both. Following are the sample values returned by Read() as recorded on each. Please share your opinion on what maybe going wrong. I have also pasted the code of long HX711::read() below. I have modified it to use bit operations than nested for loop to form the 24 bit value. Kindly check.

As recorded on Arduino:

HXvLOG:HX711Read():RetVal 8315205 HXvLOG:HX711Read():RetVal 8315038 HXvLOG:HX711Read():RetVal 8314981 HXvLOG:HX711Read():RetVal 8315190 HXvLOG:HX711Read():RetVal 8315342 HXvLOG:HX711Read():RetVal 8315221 HXvLOG:HX711Read():RetVal 8315107 HXvLOG:HX711Read():RetVal 8314698 HXvLOG:HX711Read():RetVal 8315440 HXvLOG:HX711Read():RetVal 8315588

As recorded on other board(NRF52832)

HXvLOG:HX711Read():RetVal 16746359 HXvLOG:HX711Read():RetVal 16746363 HXvLOG:HX711Read():RetVal 16746310 HXvLOG:HX711Read():RetVal 16746301 HXvLOG:HX711Read():RetVal 16746294 HXvLOG:HX711Read():RetVal 16746298 HXvLOG:HX711Read():RetVal 16746318 HXvLOG:HX711Read():RetVal 16746303 HXvLOG:HX711Read():RetVal 16746311 HXvLOG:HX711Read():RetVal 16746311

Following is the source code -->

long HX711::read() {

byte readVal=0;
long retVal=0;

// wait for the chip to become ready
while (!is_ready())
    {
    }

for (int j = 23; j >= 0; j--) 
{
    digitalWrite(PD_SCK, HIGH);
    readVal=digitalRead(DOUT);
    digitalWrite(PD_SCK, LOW);

    retVal |= readVal & 0x01;
    retVal = retVal << (1 && j);

}
for (int i = 0; i < GAIN; i++) 
{
    digitalWrite(PD_SCK,HIGH);
    digitalWrite(PD_SCK,LOW);
}

retVal^=0x800000;

//Serial.print(F("HXvLOG:HX711Read():RetVal %d Diff %d\n",retVal, (retVal-pHX711State->OFFSET));          
Serial.print(F("HXvLOG:HX711Read():RetVal "));
Serial.print(retVal);
Serial.print(F(" Diff "));
Serial.println((retVal-OFFSET));
return retVal;

}

electrokean commented 7 years ago

What exactly are you trying to do with this operation? retVal = retVal << (1 && j); I'd guess you're trying to avoid shifting on the last bit. That would multiply your reading by 2, which is what appears to be happening.

Surely it is easier to have the loop do this (swap the order):

retVal = retVal << 1;
retVal |= readVal & 0x01;

The first shift does nothing as the retVal starts with 0.

mashhashkarma commented 7 years ago

retVal = retVal << (1 && j);
will reduce to retVal = retVal << 0 ; when j becomes 0. I didnt quite undersatand how this is result in multiplication by 2.

However,the suggestion you made to swap the statements is valid.

mashhashkarma commented 7 years ago

typo correction "...undersatand how this would result in ... "

electrokean commented 7 years ago

Agreed, it seems like it "should" work, but it depends on how the compiler treats logical values. Pretty sure that isn't clearly defined. A safe alternative might have been: retVal = retVal << (j?1:0);

mashhashkarma commented 7 years ago

As per HX711 datasheet the range of values should be between 800000h (MIN) and 7FFFFFh (MAX). But as you can see i'm getting 8 digit values on the other board, though in Arduino it is 7 digit. Note that the same code is running on both the MCU's (both are ARM COrtex M0).

mashhashkarma commented 7 years ago

I can try retVal = retVal << (j?1:0); But not convinced it will solve. Is there any other cause you can think of that can result in this kind of behavior?

electrokean commented 7 years ago

So you're running exactly the same code on both? Both via Arduino IDE? Same gcc version? You say both are running Cortex M0, so which "Arduino" are you using? Same CPU speed?

The only thing that worried me in you original code I highlighted, as it could have led to incorrect shifting. I've seen various compiler bugs over the years, including problems with shifting by 0.

In reality you should be sign extending the values = i.e. 800000h => FF800000h to get a 32-bit signed. That's what this code in the library is for (which you left out):

    // Replicate the most significant bit to pad out a 32-bit signed integer
    if (data[2] & 0x80) {
        filler = 0xFF;
    } else {
        filler = 0x00;
    }

    // Construct a 32-bit signed integer
    value = ( static_cast<unsigned long>(filler) << 24
            | static_cast<unsigned long>(data[2]) << 16
            | static_cast<unsigned long>(data[1]) << 8
                        | static_cast<unsigned long>(data[0]) );

Just picking two of your example readings: 8315440 is 7EE230h, and doesn't need sign extending - but seems awfully close to the max conversion range limit, so it doesn't seem valid. 16746303 is FF873Fh, and extended that is FFFF873Fh = -30913 decimal - which looks perfectly valid.

Something you're doing must be different for these to be read differently from the HX711 on the two platforms.

electrokean commented 7 years ago

BTW retVal = retVal << (j?1:0); was just a suggested alternative to your original logic, because I believe (1 && j) is not guaranteed to return 0 or 1 (just 0 and non-zero). But that could still trip up a compiler or CPU that doesn't like shifting by zero. I'd stick with the shift prior to OR'ing the next LSB.

mashhashkarma commented 7 years ago

Hi, sorry for the delayed response, was sucked into something else. Firstly i wish to correct myself and say i'm using Arduino UNO and NRF52832 (has Cortex M0). I did an experiment of simply tabulating the values read from HX711 when using Arduino and when using NRF. I see some bizarre stuff happening and request all to help me understand and fix the matter. I have posed some questions below the table.

SETUP

The same Loadcell and HX711 is used. Once Loadcell is connected to HX711 it remains that way. HX711 connected to NRF first and readings taken. Then the wire connection from HX711 changed to Arduino and readings taken. The HX711_read() is minimized to just reading the bits from HX711 i.e the read value is not modified (not even XOR'ing with 0x8000). Have pasted the code at the end.

TABULATED READINGS

image

QUESTIONS

a) Why is the HX711 readings -ve when read via Arduino and +ve when read via NRF(CortexMO) b) Why are the readings read via Arduino double that of readings read from NRF(Cortex MO) c) When using NRF, the MAX value 0x7FFFFF(as per spec) is crossed when the 300gms replaced with 400 gms. Similarly the MIN value of 0x800000(as per spec) is crossed when using Arduino when the 300gms replaced with 400 gms.In both cases the values seem to restart from 0 after crossing. This is contradictory to specification which says it saturates at MIN and MAX value.

mashhashkarma commented 7 years ago

Forgot to paste the code. Here it is. long HX711::read() { byte readVal=0; long retVal=0;

// wait for the chip to become ready
while (!is_ready())
    {}

Serial.println(F("Reading HX711... "));

for (int j = 23; j >= 0; j--) 
{
    digitalWrite(PD_SCK, HIGH);
    readVal=digitalRead(DOUT);
    digitalWrite(PD_SCK, LOW);

    retVal = retVal << 1;
    retVal |= readVal & 0x01;

}

// set the channel and the gain factor for the next reading using the clock pin
for (int i = 0; i < GAIN; i++) 
{
    digitalWrite(PD_SCK,HIGH);
    digitalWrite(PD_SCK,LOW);
}

Serial.print(F("HXvLOG:HX711Read():RetVal "));
Serial.print(retVal);
Serial.print(F(" Diff "));
Serial.println((retVal-OFFSET));
return retVal;

}

electrokean commented 7 years ago

The values returned by the Arduino look completely normal. The values read from the HX711 need to be sign extended as I've explained above. So 0x800000 is actually 0xff800000 or -8388608 decimal, and 0x7fffff is 8388607, giving a full 24 bit range of 16777216. Of course, you don't typically get that full dynamic range.

Thus, your values in the table above starting with FF when sign extended to 32 bit will be smaller negative values. At 0g you have 0xfffedc77 which is -74633. That is your zero offset (tare), and a small negative offset is nothing to worry about.

At 700g you have 94294 - -74633 or a total of 168927, which means approx 241 counts per gram.

Why your code on the Cortex M0 isn't working I can't exactly explain, nor have the time to try reproduce, but it clearly is only half the expected value. The only thought I have other than a hard to spot bug, is that the Cortex M0 is pulsing the clock and reading the result too fast for the HX711. Actually, you could try moving the digitalRead() to after the falling pulse on PD_SCK and see if that helps.

for (int j = 23; j >= 0; j--) 
{
    digitalWrite(PD_SCK, HIGH);
    digitalWrite(PD_SCK, LOW);
    readVal=digitalRead(DOUT);

    retVal = retVal << 1;
    retVal |= readVal & 0x01;           
}

Any reason you aren't using the code from the library (which is known to work, and does sign extension), rather than writing your own read function? If you're asking for support on this library, but are actually using your own code, the least you could do is test if the actual library read function give the expected results?

mashhashkarma commented 7 years ago

hi there, In my earlier experiment, i was trying to analyse and compare the 24 bits read from HX711 in case of Arduino and NRF, hence did not use the library code. This time i used the library code with only change that instead of ShiftIn() i have read bits into data[ ]. I Have done this so that same code i can run on both Arduino and NRF. The code is later on below. Well, on Arduino everything works well, as you can see in the table below i.e readings are fine, weight measurements are coming out well. However, on using same code on NRF, the readings are not good, as you can see in the table below. By the way i did take in your suggestion of moving the digitalRead() to after the falling pulse on PD_SCK.

Any other suggestions you may have ? If you would like to run some experiments, please tell me , i can do it.

image

Below is the code of HX711::read(). long HX711::read() { uint8_t readVal=0; unsigned long value = 0; uint8_t data[3] = { 0 }; uint8_t filler = 0x00;

// wait for the chip to become ready
while (!is_ready());

// pulse the clock pin 24 times to read the data
// read MSB
for (int j = 0; j <8; j++) 
{
    digitalWrite(PD_SCK, HIGH);
    readVal=digitalRead(DOUT);
    digitalWrite(PD_SCK, LOW);
        data[i] = data[i]<< 1;
    data[i] |= readVal & 0x01;
}
//read second byte
for (int j = 0; j <8; j++) 
{
    digitalWrite(PD_SCK, HIGH);
    readVal=digitalRead(DOUT);
    digitalWrite(PD_SCK, LOW);
    data[1] = data[1]<< 1;
    data[1] |= readVal & 0x01;
}
//read third byte
for (int j = 0; j <8; j++) 
{
    digitalWrite(PD_SCK, HIGH);
    readVal=digitalRead(DOUT);
    digitalWrite(PD_SCK, LOW);
    data[0] = data[0]<< 1;
    data[0] |= readVal & 0x01;
}
// set the channel and the gain factor for the next reading using the clock pin
for (int i = 0; i < GAIN; i++) {
    digitalWrite(PD_SCK, HIGH);
    digitalWrite(PD_SCK, LOW);
}

// Replicate the most significant bit to pad out a 32-bit signed integer
if (data[2] & 0x80) {
    filler = 0xFF;
} else {
    filler = 0x00;
}

// Construct a 32-bit signed integer
value = ( static_cast<unsigned long>(filler) << 24
        | static_cast<unsigned long>(data[2]) << 16
        | static_cast<unsigned long>(data[1]) << 8
        | static_cast<unsigned long>(data[0]) );

 return (static_cast<long>(value));

}

electrokean commented 7 years ago

It just doesn't appear to be reading all 24 bits. Do you have access to a logic analyser to capture the signals and timing? I probably have some nrf52832 around somewhere, but no time to search for them or set up a development environment to try this. Afraid I have no more suggestions right now. Maybe someone else here has an idea.

bogde commented 7 years ago

i think i have some nrf52832 too, and a (cheap) logic analyser, i'll try to look into this if time permits. thanks @electrokean for all your effort.

bogde commented 7 years ago

i just checked and unfortunately i don't have the NRF52832 board. sorry.

mashhashkarma commented 7 years ago

hi Electrokean adn Bogde , Thanks a lot for your efforts/help. I dont have a logic analyser. I thought i'll buy this one http://www.ebay.in/itm/302150017754?aff_source=Sok-Goog . DOes this help ? Please tell me what experiment i can do with the logic analyser. Since you dont have the nrf52832, i can do the experiment and post you data.

electrokean commented 7 years ago

@mashhashkarma yes, that will help. Buying a real Saleae instead of a clone would be better of course. You can then capture the signals on the clock and data pins in both setups and post images or make the capture files available for download and review. You may even spot the issue yourself.

bogde commented 7 years ago

hi @mashhashkarma , did you solve the issue? did you get the logic analyzer? do you still need any help with this?

fyi, i only have this: http://dangerousprototypes.com/blog/open-logic-sniffer/ (i'm not sure if it's better or worse than the one you found)

mashhashkarma commented 7 years ago

hi Bogde, havent got the chance to try out the logic analzer yet .... been sucked into some other urgent matters. I would need help after running the anlayser...but it will take time. Can this thread be open for longer ? or if its a problem, its there a way i can open it again later instead of creating a new issue ?

bogde commented 7 years ago

sure, no problem, i'll keep it open.

pvonmoradi commented 3 years ago

@bogde Is your library supported on nRF52? I'm using this core: https://github.com/sandeepmistry/arduino-nRF5 . To my understanding, as long as they have implemented Arduino HAL, all libraries should work on target. Isn't it right? If so why is there a https://github.com/bogde/HX711#hal-support in the readme?

amotl commented 3 years ago

Hi Pooya,

[1] lists two items regarding support for SAMD, however those have not been tested. We will be happy to hear if the library works properly on a nRF52, so please get back to us.

The background on needing to implement specific support for ESP8266 and ESP32 is that those are considered to be FAST_CPUs, along with STM32 and SAMD, see also #123. That means that for those, we use the shiftInSlow() function in order to slow down the bitbanging needed to properly read out the HX711.

https://github.com/bogde/HX711/blob/75dfed4cf54d9067bb5c7b897a58c2576b90e376/src/HX711.cpp#L36-L62

With kind regards, Andreas.

[1] https://github.com/bogde/HX711#hal-support

pvonmoradi commented 3 years ago

@amotl Hi Andreas, thanks for your quick reply :) I have tested a HX711 with a custom board designed around nRF52832 using (https://github.com/sandeepmistry/arduino-nRF5) core. Previously, there was an error in which the communication between MCU and ADC was faulty. I can't replicate that now and the library seem to be working fine.
This MCU is running @64MHz (similar to a bluepill) so I guess special consideration should be made to lower the transactions speeds?
Here is a snippet I'm using to read the value of a two load cell bridge setup.

void printHX711() {
    // TODO check if available here instead of true
    if (true) {
        Serial.print("Reading: ");
        r_weight = scale.get_units(1);
        /* if (units < 0) { */
        /*     units = 0.00; */
        /* } */
        Serial.print(r_weight);
        Serial.print(" grams");
        Serial.print(" calibration_factor: ");
        Serial.println(calibration_factor);
    } else {
        Serial.println("HX711 not found.");
    }
}

Here are the snapshots from my logic analyzer in four zoom levels. D0: SCK D1: DT The jumps in D0 appear to be in sync with the 1000ms delay in sampling in main loop. image image image image

If I need to perform other experiments or if there is a "stress-test script" that should be tested, don't hesitate to ask me.
Thanks

amotl commented 3 years ago

Hi Pooya,

sorry that I am probably not able to contribute something significant here because I am not an electronic engineer at all.

However, from a software perspective, have you been able to confirm that the code running on your MCU actually uses the shiftInSlow() method for reading the signal?

If so, you might want to fiddle with the delayMicroseconds() procedure. Maybe this will be able to get you some better readings when adjusting that delay to match your MCU speed.

With kind regards, Andreas.