I’m trying to build a scale with ESPhome.
The Goal:
I have two snakes where a scale will routinely be used to weigh the snakes and their feed. My idea was to create the scale with a button for each snake to log feed size and separate button to log the snake’s weight. The fifth button for running a manual tare on the scale.
The Problem:
The values off the HX711 are varying wildly so I cannot get a baseline reading. I’m not sure if the problem is the amp or something else. For example, the baseline reading 30 minutes ago was ‘1315912’ and it’s now at ‘1259202’'.
The other kick in the pants is that the reading is changing every time I reinstall the firmware. For example: I’m sitting at ‘1184776’ for 0 weight and ‘1935727’ for 4572 g (dumbbell). I set those values and reinstalled the firmware. Now 0 g weight is showing ‘1286100’
I’ve twisted the wire pairings between the load cell and the amp to help reduce EMI based on recommendations from a few forum threads. I am powering the ESP from USB (wall plug) and using the VIN pin off the ESP to power to HX711.
Setup:
10 kg load cell and HX711
ESP32 board
Freenove 16x2 i2c display
Five momentary pushbuttons
3D printed enclosure
YAML:
Pulled from here and modified to add the display and buttons.
## Node configuration ##
substitutions:
name: esphome-web-7dd534
friendly_name: ESPHome Web 7dd534
baseline_0_reading: "1315912"
baseline_4572_reading: "2078353"
esphome:
name: ${name}
friendly_name: ${friendly_name}
name_add_mac_suffix: False
esp32:
board: esp32dev
framework:
type: esp-idf
globals:
- id: initial_zero
type: float
restore_value: yes
# NOTE: make sure to align this value to the one used in "calibrate_linear" below!
initial_value: ${baseline_0_reading}
- id: auto_tare_enabled
type: bool
restore_value: yes
initial_value: 'true'
- id: auto_tare_difference
type: float
restore_value: yes
initial_value: '0'
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
api:
# Enable logging
logger:
level: INFO
ota:
platform: esphome
status_led:
pin:
number: GPIO2
inverted: True
i2c:
sda: GPIO21
scl: GPIO22
display:
- platform: lcd_pcf8574
dimensions: 16x2
address: 0x27
lambda: |-
it.printf("Weight: %6.0f g", id(smart_scale_hx711_value).state);
button:
## Button used to initiate a manual tare
- platform: template
id: smart_scale_manual_tare_action_switch
name: "Smart Scale Manual Tare Action"
icon: mdi:scale-balance
on_press:
- lambda: |-
id(auto_tare_difference) = id(initial_zero) - id(smart_scale_hx711_value_raw).state;
- sensor.template.publish:
id: smart_scale_hx711_value_raw_diagnostic
state: !lambda 'return id(smart_scale_hx711_value_raw).state;'
switch:
## Switch to enable/disable the auto tare feature
- platform: template
id: smart_scale_continuous_tare_enabled
name: "Smart Scale Continuous Tare Enabled"
entity_category: "config"
lambda: |-
return id(auto_tare_enabled);
turn_on_action:
- lambda: |-
id(auto_tare_enabled) = true;
turn_off_action:
- lambda: |-
id(auto_tare_enabled) = false;
## Sensor Configuration ##
sensor:
# template sensors from global variables
- platform: template
id: smart_scale_initial_zero
name: "Smart Scale Initial Zero"
lambda: |-
return id(initial_zero);
update_interval: 1s
- platform: template
id: smart_scale_auto_tare_difference
name: "Smart Scale Auto Tare Zero Calibration Offset"
lambda: |-
return id(auto_tare_difference);
update_interval: 1s
- platform: template
id: smart_scale_auto_tare_deviance
entity_category: "diagnostic"
name: "Smart Scale Calibration Deviance"
lambda: |-
return (int((id(smart_scale_hx711_value_raw).state - (id(initial_zero) - id(auto_tare_difference)) ) / 100)) * 100;
update_interval: 1s
# sensors imported from home assistant
- platform: homeassistant
id: homeassistant_initial_zero
entity_id: input_number.smart_scale_initial_zero
on_value:
then:
- lambda: |-
id(initial_zero) = x;
# RAW Scale input
- platform: hx711
id: smart_scale_hx711_value_raw
internal: True
dout_pin: GPIO16
clk_pin: GPIO4
gain: 128
update_interval: 0.2s
filters:
- quantile:
window_size: 10
send_every: 1
send_first_at: 1
quantile: .9
on_value:
then:
- sensor.template.publish:
id: smart_scale_hx711_value
state: !lambda 'return id(smart_scale_hx711_value_raw).state;'
- if:
condition:
- lambda: |-
auto n = id(smart_scale_hx711_value_raw_diagnostic).state;
auto n_str = to_string(n);
return str_equals_case_insensitive(n_str, "NaN");
then:
- sensor.template.publish:
id: smart_scale_hx711_value_raw_diagnostic
state: !lambda 'return id(smart_scale_hx711_value_raw).state;'
- if:
condition:
and:
- lambda: 'return id(auto_tare_enabled);'
# current smart scale value is below approx. 10KG (raw value -275743) aka nobody is standing on the scale
# - lambda: 'return id(smart_scale_hx711_value).state < 10.0;'
# only update if the current deviance is not 0
- lambda: 'return id(smart_scale_auto_tare_deviance).state != 0.0;'
then:
- if:
condition:
# current raw scale value is below expected zero value
- lambda: 'return id(smart_scale_hx711_value_raw).state < (id(initial_zero) - id(auto_tare_difference));'
then:
# INcrease Auto-Tare offset to slowly align real zero value with expected zero value
- lambda: |-
id(auto_tare_difference) += 10;
else:
# DEcrease Auto-Tare offset to slowly align real zero value with expected zero value
- lambda: |-
id(auto_tare_difference) -= 10;
- platform: template
id: smart_scale_hx711_value_raw_diagnostic
name: "Smart Scale HX711 Raw Value"
entity_category: "diagnostic"
# Mapped value to KG
- platform: template
id: smart_scale_hx711_value
name: "Smart Scale HX711 Value"
internal: False
filters:
# apply auto_tare difference
- lambda: 'return x + id(auto_tare_difference);'
# apply rough calibration
- calibrate_linear:
# retrieve these values by evaluating the raw values with loads of known mass.
# note that a bigger difference between measurements usually results in higher resolution,
# so measure 0 Kg and the highest known mass you have (like f.ex. your own weight, measured by a normal scale with good accuracy)
- ${baseline_0_reading} -> 0
- ${baseline_4572_reading} -> 4572
# map values below 0.1 to 0 (to decrease value changes due to random fluctuation)
- lambda: |-
if (x <= 0.1) {
return 0.0;
} else {
return x;
}
unit_of_measurement: g
# the sensor will automatically update through the 'sensor.template.publish' call
update_interval: 5s
binary_sensor:
- platform: gpio
pin:
number: GPIO27
mode: INPUT_PULLUP
inverted: True
name: "Button 1"
on_press:
button.press: smart_scale_manual_tare_action_switch
- platform: gpio
pin:
number: GPIO25
mode: INPUT_PULLUP
inverted: True
name: "Button 2"
- platform: gpio
pin:
number: GPIO26
mode: INPUT_PULLUP
inverted: True
name: "Button 3"
- platform: gpio
pin:
number: GPIO32
mode: INPUT_PULLUP
inverted: True
name: "Button 4"
- platform: gpio
pin:
number: GPIO33
mode: INPUT_PULLUP
inverted: True
name: "Button 5"