fivesixzero / CircuitPython_HX711

A CircuitPython driver for the HX711 load cell amplifer and ADC
MIT License
5 stars 1 forks source link

read() returns values differently from raw calculation #4

Open utdrmac opened 1 year ago

utdrmac commented 1 year ago

In my code, I'm taking read(10) along with read_raw() and storing to InfluxDB. Every now-and-then, the read() calculation is extremely far off from a self-calculated value using raw().

My scale is under constant load; under a beehive. The offset value for init was calculated with nothing on the scale. The scalar was determined with a 5lb dumbbell, thus my unit is Lbs.

My code:

    hx = HX711_GPIO(gpio_data, gpio_clk, tare=False, offset=-155118, scalar=8949.0)

    # loop forever
    while True:
        # Take 10 ADC readings
        reading = hx.read(10)
        reading_raw = hx.read_raw()

        reading = round(reading, 2)

        # InfluxDB Line Format
        weight_line = f"weight value={reading},raw={reading_raw} {tsn}\n"
        mqtt.publish(weight_line)

        time.sleep(5)

Resulting grafana graph:

Screen Shot 2023-05-03 at 1 26 45 PM

For completeness, the raw dataline above is calculated within Grafana when extracting the data from InfluxDB:

from(bucket: "beehive")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r["_measurement"] == "weight")
  |> filter(fn: (r) => r["_field"] == "raw")
  |> map(fn: (r) => ({r with _value: (r._value + 155118.00) / 8949.00}))
  |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
  |> yield(name: "mean")

You can see the map() line where I perform the same calculation as here. This calculation should return the same value as read(), but as you can see on this zoomed in graph, the value from read(10) is much higher than my raw-manual calculation (63.11 vs 55.18).

Screen Shot 2023-05-03 at 1 30 06 PM

I can see the datapoint in Influx as 63.11, so I know this is not some artifact of Grafana extrapolation.

Screen Shot 2023-05-03 at 1 36 23 PM

Any thoughts on why this happens sometimes with read()?

fivesixzero commented 1 year ago

Thanks for posting this, sorry you're having issues with the HX711 library. :(

Its been awhile since I last dug into the internals of this library's GPIO read_raw() ops but if I recall right there's always going to be some potential for occasional weird results with GPIO reads due to a variety of possible factors. I noticed this while researching odd behavior in #2 (which ended up being primarily caused by undervolting the HX711, whoops). That research led to a few changes, mostly streamlining the major bitbang ops to make outlier results less likely. This kind of non-deterministic behavior was the main reason I worked out an RP2040 PIO version of the driver, which is potentially much more reliable across a large number of operations.

So one likely suspect might involve CircuitPython doing things in the background (GC, wireless/network stuff, etc) while the read() op is doing timing-sensitive work, causing clocking during gain-set and read ops to be a bit wonky on just one read out of many, on an infrequent basis. I had considered implementing some kind of "major outlier removal" pre-averaging during read() but ended up bailing on the idea in order to keep things simple.

There could be a few other things going on that could potentially cause issues though, so it'd be helpful to get a broader understanding of what your setup looks like before diving in too deep.

utdrmac commented 1 year ago

Thanks for handling my "support" request. :) I'm using a RPi Pico W. I upgraded to CP 8.1.0-beta2 just a couple days ago. I'm using this kit.

I too was thinking about adding outlier logic. My beehive's weight should never change by more than 1/10th Lb within 5s.

fivesixzero commented 1 year ago

Since you're using a Pico W I'd recommend trying the PIO version of the drvier instead of GPIO. In my testing it was a more reliable option than the GPIO version on RP2040-based boards.

Still might be worth implementing outlier logic. With CircuitPython libs I'm always anxious about filesize and RAM usage given the very limited RAM on many platforms, so it might not be good to include in the library itself, maybe some code in an example might be good though.