nec-k
(Kostya)
January 16, 2025, 3:45pm
1
Hello everyone.
I use an ultrasonic sensor to determine the filling of a barrel.
Using Filter, I converted the depth data to percentages. And I only get data in %.
But how can I make it also transmit standard data in distances in meters from the same sensor?
sensor:
- platform: ultrasonic
trigger_pin: GPIO12
echo_pin: GPIO14
name: Distance1
id: distance1
update_interval: 60s
timeout: 2.0m
filters:
- lambda: |-
if (x > 1.45) {
return 0;
} else if (x < 0.3) {
return 100;
} else {
return (1.45-x) / (1.45-0.3) * 100.0;
}
unit_of_measurement: "%"
I hope for help=)
Hellis81
(Hellis81)
January 16, 2025, 3:49pm
2
I think if you keep this as is, then add a new template sensor and add the filter to that sensor.
Karosm
(Karosm)
January 16, 2025, 5:58pm
3
You can create a dumb template sensor and pass the raw value from your ultrasonic to it:
on_raw_value:
then:
- sensor.template.publish:
id: distance
state: !lambda "return x;"
sensor:
- platform: template
name: "Distance Sensor"
id:distance
update_interval: never
You should probably use a copy sensor rather than a template sensor these days. It’s the newer way to do things.
1 Like
Karosm
(Karosm)
January 16, 2025, 8:39pm
5
Nice!
Does it output raw or filtered?
nec-k
(Kostya)
January 16, 2025, 8:47pm
6
Thanks for the right way, I figured it out with the first example.
platform: template
my code just in case, what happened:
- platform: ultrasonic
trigger_pin: GPIO12
echo_pin: GPIO14
name: Distance1m
id: distance1m
update_interval: 60s
unit_of_measurement: "m"
- platform: template
name: "Distance1"
id: Distance1
lambda: |-
if (id(distance1m).state > 1.45) {
return 0;
} else if (id(distance1m).state < 0.3) {
return 100;
} else {
return (1.45-id(distance1m).state) / (1.45-0.3) * 100.0;
}
unit_of_measurement: "%"
accuracy_decimals: 2
update_interval: 60s
It just copies the output of the sensor you point it at. So if the copied sensor already has filters on it it would take the filtered output.
I typically keep a “raw” version and maybe mark it as internal: true
when I’ve done testing if I don’t really need it to go to HA.
Sometimes I even chain a few variants (say raw, processed, and one as a %).
You pretty much just use them like you would when “making a copy via a template sensor”. I think they were implemented to make that simpler.
1 Like
The copy version of this would look something like this.
- platform: ultrasonic
trigger_pin: ${ultra_sonic_trigger_pin} # Yellow wire
echo_pin: ${ultra_sonic_echo_pin} # Green wire
name: "Tank Level"
id: hcsr04
entity_category: diagnostic
update_interval: never
internal: false
accuracy_decimals: 1
unit_of_measurement: 'cm'
timeout: 1m
pulse_time: 10us
filters:
- multiply: 100 # Convert to cm
- median: # Moving median to smooth noise.
window_size: 10
send_every: 10
send_first_at: 10
#Convert the hcsr04 distance to a water tank level (% full)
- platform: copy
source_id: hcsr04
id: water_tank_level
internal: false
name: Water Tank Level
unit_of_measurement: '%'
accuracy_decimals: 1
entity_category: ''
filters:
# Map from distance to % full. To calibrate.
- calibrate_linear:
- 3 -> 100
- 33 -> 0
# Overide values less than 0% and more than 100%. Round to 5%.
- lambda: |
if (x > 100) return 100;
else if (x < 0) return 0;
else return ceil(x / 5) * 5;
nec-k
(Kostya)
January 17, 2025, 10:10am
9
Great, we’ll use this advice too.
sensor:
- platform: ultrasonic
trigger_pin: GPIO12
echo_pin: GPIO14
name: Distance1m
id: distance1m
update_interval: 60s
timeout: 2.0m
unit_of_measurement: "m"
accuracy_decimals: 3
filters:
- sliding_window_moving_average:
window_size: 20
send_every: 1
- platform: template
name: "Distance1"
id: Distance1
lambda: |-
if (id(distance1m).state > 1.45) {
return 0;
} else if (id(distance1m).state < 0.3) {
return 100;
} else {
return (1.45-id(distance1m).state) / (1.45-0.3) * 100.0;
}
unit_of_measurement: "%"
accuracy_decimals: 2
update_interval: 60s
filters:
- sliding_window_moving_average:
window_size: 20
send_every: 1
- platform: copy
source_id: distance1m
id: tank1_Liters
name: tank1_Liters
unit_of_measurement: 'L'
accuracy_decimals: 1
filters:
- calibrate_linear:
- 1.45 -> 0
- 0.3 -> 903
nec-k
(Kostya)
January 17, 2025, 10:14am
10
Guys! How to turn the variable “distance1m” from positive to negative?
The thing is that the depth of the distance to the surface is visually better taken in the direction of decreasing
Hellis81
(Hellis81)
January 17, 2025, 10:15am
11
multiply with negative one
I needed the characters to post
1 Like
BTW I find median much better than average (per my example) as a measure of central tendancy as average is sensitive to spikes/outliers whereas median isn’t). It’s a “Robust measure”.
From the web…
You can also use clamp
for capping values although I forget to use that myself.
There is also multiply
.
As you can see there is typically a native ESPHome way and a lambda way.
Sometimes layering up the filters the ESPHOME way can make things easier to read and debug. Although some prefer lambda and sometimes it can be more succinct.
nec-k
(Kostya)
January 18, 2025, 6:38am
13
Thank you my friend. I made the changes and will keep an eye on it
1 Like
Wow that really demonstrates the difference!
nec-k
(Kostya)
January 19, 2025, 6:28am
15
As a result, with simple calculations I got the height => the volume of the tank in meters => in liters.
It is interesting now to compare the data now = there were 100 liters,
after 10 minutes, it became 90 liters = the output of the variable: 10 liters consumed in 10 minutes.
How to make the variables compare in a given period of time…
make Home Assistant statistics, state changes ±