DaneSlattery / hx711

A no-std rust library for the ESP32
GNU General Public License v3.0
6 stars 5 forks source link

Negative readings when there is no load applied #12

Closed weiying-chen closed 5 months ago

weiying-chen commented 6 months ago

This is the code I'm using:

use esp_idf_svc::hal::{
    delay::{Delay, FreeRtos},
    gpio::PinDriver,
    peripherals::Peripherals,
};

use loadcell::{hx711::HX711, LoadCell};

fn main() {
    esp_idf_svc::sys::link_patches();
    esp_idf_svc::log::EspLogger::initialize_default();

    let peripherals = Peripherals::take().unwrap();
    let dt = PinDriver::input(peripherals.pins.gpio2).unwrap();
    let sck = PinDriver::output(peripherals.pins.gpio3).unwrap();
    let delay = Delay::new_default();

    let mut load_sensor = HX711::new(sck, dt, delay);

    load_sensor.tare(16);
    load_sensor.set_scale(1.0);

    loop {
        if load_sensor.is_ready() {
            let reading = load_sensor.read_scaled().unwrap();
            // let reading = load_sensor.read().unwrap(); // Use this one to calibrate the load cell
            log::info!("Weight: {:.0} g", reading);
            // log::info!("Weight: {} g", reading); // Use this to get all the decimals
        }

        FreeRtos::delay_ms(1000u32);
    }
}

It doesn't matter what number I pass to load_sensor.tare, the value is never 0, and it's often negative:

I (12955) rust_esp32_hx711: Weight: 0.027999999 g
I (13955) rust_esp32_hx711: Weight: -0.0924 g
I (14955) rust_esp32_hx711: Weight: -0.0756 g
I (15955) rust_esp32_hx711: Weight: -0.0056 g
I (16955) rust_esp32_hx711: Weight: 44.814 g // I applied a load here
I (17955) rust_esp32_hx711: Weight: 44.8364 g // I applied a load here
I (18955) rust_esp32_hx711: Weight: 44.786 g // I applied a load here
I (19955) rust_esp32_hx711: Weight: 45.429996 g // I applied a load here
I (20955) rust_esp32_hx711: Weight: 0 g
I (21955) rust_esp32_hx711: Weight: 0.0924 g

This is another load cell of mine (again, with no load applied):

I (3805) rust_esp32_bme280: Weight: -3.9663 g
I (4805) rust_esp32_bme280: Weight: -3.9312 g
I (5805) rust_esp32_bme280: Weight: -3.8205001 g
I (6805) rust_esp32_bme280: Weight: -3.9609 g
I (7805) rust_esp32_bme280: Weight: -3.7773001 g
I (8805) rust_esp32_bme280: Weight: -3.7692 g
I (9805) rust_esp32_bme280: Weight: -3.7935002 g

Is there an issue with tare? Or maybe the issue is my load cell? Or I'm supposed to filter out the negative values by myself?

beeb commented 6 months ago

Does it help if you add critical_section to your dependencies and copy the following file into your project? https://github.com/beeb/coffee-scale-app/blob/main/rs/src/critical_section.rs

Not seeing it in your example above.

beeb commented 6 months ago

Note that since your tare on startup, it's possible that you affect the load cell when providing power to your board and some of the readings are not in an unloaded state. You could check out my scale repo to see how I implemented a check for stability before taring. https://github.com/beeb/coffee-scale-app/blob/2e0c30409ef3bcbafa4e4cbbd71f1dcc0d413a50/rs/src/weight.rs#L92 (this assumes you've tuned your scaling factor to represent grams)

The first example above looks ok to me. It's reasonable to expect a variance in the range of 0.1g due to noise and environment.

weiying-chen commented 6 months ago

I added the critical_section code:

use esp_idf_svc::hal::{
    delay::{Delay, FreeRtos},
    gpio::PinDriver,
    peripherals::Peripherals,
};

use esp_idf_svc::hal::interrupt::{IsrCriticalSection, IsrCriticalSectionGuard};
use loadcell::{hx711::HX711, LoadCell};
use std::sync::Mutex;

static CS: IsrCriticalSection = IsrCriticalSection::new();
static CS_GUARD: Mutex<Option<IsrCriticalSectionGuard>> = Mutex::new(None);

pub struct EspCriticalSection {}

unsafe impl critical_section::Impl for EspCriticalSection {
    unsafe fn acquire() {
        let mut guard = CS_GUARD.lock().unwrap();
        *guard = Some(CS.enter());
    }

    unsafe fn release(_token: ()) {
        let mut guard = CS_GUARD.lock().unwrap();
        *guard = None;
    }
}

critical_section::set_impl!(EspCriticalSection);

fn main() {
    esp_idf_svc::sys::link_patches();
    esp_idf_svc::log::EspLogger::initialize_default();

    let peripherals = Peripherals::take().unwrap();
    let dt = PinDriver::input(peripherals.pins.gpio2).unwrap();
    let sck = PinDriver::output(peripherals.pins.gpio3).unwrap();
    let delay = Delay::new_default();

    let mut load_sensor = HX711::new(sck, dt, delay);

    load_sensor.tare(16);
    load_sensor.set_scale(0.0043);

    loop {
        if load_sensor.is_ready() {
            let reading = load_sensor.read_scaled().unwrap();
            log::info!("Weight: {} g", reading); // Use this to get all the decimals
        }

        FreeRtos::delay_ms(1000u32);
    }
}

I'm getting the same negative readings:

// 1 kg load cell:

(8145) rust_esp32c3_hx711: Weight: -0.15910001 g
I (9145) rust_esp32c3_hx711: Weight: 0.1333 g
I (10145) rust_esp32c3_hx711: Weight: -0.0215 g

// 2 kg load cell:

I (17765) rust_esp32c3_hx711: Weight: -4.4376 g
I (18765) rust_esp32c3_hx711: Weight: -4.214 g
I (19765) rust_esp32c3_hx711: Weight: -4.3473 g

But at least the readings are more stable, I think.

Okay, I'll check your repo and try to implement something. Thanks for the suggestion.

So, since my first example looks okay, I could just filter out the negative numbers?

weiying-chen commented 6 months ago

I tried to incorporate your wait_stable function:

use std::collections::VecDeque;

use esp_idf_svc::hal::{
    delay::{Delay, Ets, FreeRtos},
    gpio::{Gpio2, Gpio3, Input, Output, PinDriver},
    peripherals::Peripherals,
};

use loadcell::{hx711::HX711, LoadCell};

mod critical_section;

const LOADCELL_STABLE_READINGS: usize = 10;
const LOADCELL_LOOP_DELAY_US: u32 = 10000;
const LOADCELL_READY_DELAY_US: u32 = 1000;

fn wait_stable(
    load_sensor: &mut HX711<PinDriver<'_, Gpio3, Output>, PinDriver<'_, Gpio2, Input>, Delay>,
) {
    let mut readings: VecDeque<f32> = VecDeque::with_capacity(LOADCELL_STABLE_READINGS);
    loop {
        while !load_sensor.is_ready() {
            Ets::delay_us(LOADCELL_READY_DELAY_US)
        }
        let reading = load_sensor.read_scaled().expect("Failed to read scale");
        log::info!("Waiting for stable weight: {:.4}", reading);
        if readings.len() == LOADCELL_STABLE_READINGS {
            readings.pop_front();
        }
        readings.push_back(reading);
        if readings.len() == LOADCELL_STABLE_READINGS
            && readings.iter().all(|&x| (x - reading).abs() < 0.1)
        {
            break;
        }
        Ets::delay_us(LOADCELL_LOOP_DELAY_US); // Adjust delay as necessary
    }
}

fn main() {
    esp_idf_svc::sys::link_patches();
    esp_idf_svc::log::EspLogger::initialize_default();

    let peripherals = Peripherals::take().unwrap();
    let dt = PinDriver::input(peripherals.pins.gpio2).unwrap();
    let sck = PinDriver::output(peripherals.pins.gpio3).unwrap();
    let delay = Delay::new_default();
    let mut load_sensor = HX711::new(sck, dt, delay);

    wait_stable(&mut load_sensor);
    load_sensor.tare(16);
    load_sensor.set_scale(0.0043);

    loop {
        if load_sensor.is_ready() {
            let reading = load_sensor.read_scaled().unwrap();
            log::info!("Weight: {} g", reading); // Use this to get all the decimals
        }

        FreeRtos::delay_ms(1000u32);
    }
}

It gets stuck in this loop (I think it never finds a stable reading):

I (7386) rust_esp32c3_hx711: Waiting for stable weight: -1077.0000
I (7476) rust_esp32c3_hx711: Waiting for stable weight: -1077.0000
I (7576) rust_esp32c3_hx711: Waiting for stable weight: -1003.0000
I (7666) rust_esp32c3_hx711: Waiting for stable weight: -962.0000
I (7756) rust_esp32c3_hx711: Waiting for stable weight: -1034.0000
I (7846) rust_esp32c3_hx711: Waiting for stable weight: -1019.0000
I (7946) rust_esp32c3_hx711: Waiting for stable weight: -988.0000
I (8036) rust_esp32c3_hx711: Waiting for stable weight: -983.0000
I (8126) rust_esp32c3_hx711: Waiting for stable weight: -1052.0000
I (8216) rust_esp32c3_hx711: Waiting for stable weight: -1095.0000
I (8316) rust_esp32c3_hx711: Waiting for stable weight: -1052.0000
I (8406) rust_esp32c3_hx711: Waiting for stable weight: -1013.0000
I (8496) rust_esp32c3_hx711: Waiting for stable weight: -1094.0000
I (8586) rust_esp32c3_hx711: Waiting for stable weight: -1022.0000
I (8686) rust_esp32c3_hx711: Waiting for stable weight: -994.0000
I (8776) rust_esp32c3_hx711: Waiting for stable weight: -979.0000

If I remove wait_stable, I get:

I (8225) rust_esp32c3_hx711: Weight: -5.6932 g
I (9225) rust_esp32c3_hx711: Weight: -5.7792 g
I (10225) rust_esp32c3_hx711: Weight: -5.8179 g
beeb commented 6 months ago

It assumes the readings are within 0.1g of each other. If your weight is more noisy, you might need to increase that tolerance.

beeb commented 6 months ago

Your scaling factor of 1.0 is probably wrong too

weiying-chen commented 6 months ago

Ah, you're right. I changed the code:

load_sensor.set_scale(0.0043);
wait_stable(&mut load_sensor);
load_sensor.tare(16);

It gets stuck in:

I (30026) rust_esp32c3_hx711: Waiting for stable weight: -5.6889
I (30116) rust_esp32c3_hx711: Waiting for stable weight: -5.7663
I (30206) rust_esp32c3_hx711: Waiting for stable weight: -5.5513
I (30306) rust_esp32c3_hx711: Waiting for stable weight: -5.5384
I (30396) rust_esp32c3_hx711: Waiting for stable weight: -5.4997

I guess the readings never stabilize enough to break the loop. The loop does break if the threshold is 0.2 or 0.3, though. But I still get these readings without any load applied:

I (17436) rust_esp32c3_hx711: Weight: -5.7921004 g
I (18436) rust_esp32c3_hx711: Weight: -5.9469004 g
I (19436) rust_esp32c3_hx711: Weight: -6.0028 g
I (20436) rust_esp32c3_hx711: Weight: -5.8265004 g
I (21436) rust_esp32c3_hx711: Weight: -5.8265004 g

It feels like the tare function isn't doing anything.

weiying-chen commented 6 months ago

This is with my other load cell (it has small round platforms attached to it):

I (2846) rust_esp32c3_hx711: Waiting for stable weight: -415.8358
I (2936) rust_esp32c3_hx711: Waiting for stable weight: -415.8530
I (3016) rust_esp32c3_hx711: Waiting for stable weight: -415.8702
I (3106) rust_esp32c3_hx711: Waiting for stable weight: -415.6810
I (5536) rust_esp32c3_hx711: Weight: 0.19780001 g
I (6536) rust_esp32c3_hx711: Weight: 0.23220001 g
I (7536) rust_esp32c3_hx711: Weight: 0.2107 g
I (8536) rust_esp32c3_hx711: Weight: -0.0344 g
I (9536) rust_esp32c3_hx711: Weight: 0.064500004 g
I (10536) rust_esp32c3_hx711: Weight: -0.0516 g

Note: this load cell did pass the 0.1 threshold.

beeb commented 6 months ago

Looks good to me!

weiying-chen commented 6 months ago

So you would just filter out the negative zeros? E.g. so the user doesn't see:

- 0 g
0 g
-0 g
0 g
beeb commented 6 months ago

I don't know your application but yeah if you want to avoid negative values you could. Or just make it so the rounding (if any) , ignores the sign for the zero value.

weiying-chen commented 6 months ago

All right. Okay, thanks for all the help! And for being so responsive.