You are right, those probably would be a better approach and I will go that route if we can’t get this sensor to work. If we can however get this sensor to work, that would be better since I already have the sensor, it is already installed, and others have expressed interest in getting it to work also.
Well, the sensor has been here for about 2 hours, and I’ve had it working two different ways.
Not satisfied with either of the methods I’ve used, but they (and the oscilloscope traces) show that the sensor is working, and measuring distance as shown by the varying numeric results of the Arduino PulseIn() function.
Nothing useful yet, but it’s a start, and proves that it can be done.
I have several other approaches I want to try, and will then put some polish on it to make it more useful.
Here’s one of the two approaches I just used to verify function.
Nothing much defined in the YAML config except the output, and the binary_sensor, and this ‘interval’ which runs some Arduino code. It’s just a start, so don’t take this example too seriously.
You may notice I’m mixing milliseconds and microseconds, both in terminology and variable names. It’s just scaffolding at this point, and I’m not sure whether this needs to be built to run at microsecond timing, or if milliseconds will be precise enough for it. But all the useful numbers coming out of the pulseIn function are in microseconds and begin to be relevant at about 1000uS (about 6" distance) then go up to a ceiling of ~9000uS or so.
Which is why I’m going to now try it using my own code instead of pulseIn, in case I’m hitting a ceiling in that function.
globals:
- id: tx_pwm_millis
type: int
initial_value: '0'
output:
- platform: gpio
id: trigger_output
pin:
number: GPIO4
inverted: true
mode:
output: true
binary_sensor:
- platform: gpio
id: echo_receive_pin
pin:
number: GPIO5
inverted: false
mode:
input: true
sensor:
- platform: template
name: "Last Echo Diff Time"
id: last_echo_diff_time
lambda: |-
ESP_LOGD("main", "Diff milliseconds is set: %d", id(tx_pwm_millis));
return id(tx_pwm_millis);
interval:
- interval: 2sec
then:
- lambda: |-
id(trigger_output).turn_on();
delayMicroseconds(10);
id(trigger_output).turn_off();
id(tx_pwm_millis) = pulseIn(5,HIGH);
- component.update: last_echo_diff_time
The attempt using on_press/on_release and lambda’s failed miserably because they’re just too slow.
Embedding Arduino code in the lambda worked somewhat but was IMO a kludge.
Determined to make use of the existing component if at all possible, I started experimenting with various combinations of inverted GPIO’s, etc.
But the error was always “timeout exceeded”…
so… I wonder, what if I increased the timeout? (gee)
It measures now. This is working:
sensor:
- platform: ultrasonic
trigger_pin:
number: GPIO4
inverted: true
echo_pin:
number: GPIO5
name: "Ultrasonic Sensor"
update_interval: 3s
timeout: 10.0m
(modify GPIO pin assignments and update_interval to your liking.)
The trigger pulse is supposed to be a falling-edge one, so I inverted that pin.
I’m not certain of why this works, because the default timeout was for 2 metres so it should have at least measured things closer than that, but it wouldn’t.
My theory is that the code is anticipating the echo pulse to begin much sooner than it does on this device, so by increasing the timeout it waits long enough to see the receive pulse, then measures its length in microseconds, and voila!
While looking in the code of the ultrasonic component, it looked correct, like it should be working.
But the author of that component made an assumption related to the span of time designated T1 on this device’s datasheet.
They used a similar bit of distance-driven timeout logic to wait across T1 as they later use to measure the pulse length (T2) itself. But as I observed it on the scope, T1 is a very consistent 14ms no matter how far the sensor was from the target. It’s only the length of T2 that varies.
It may be that the device for which that component was written does vary T1 by distance, but this one doesn’t - it may be waiting until it’s finished receiving the echo and doing its internal math before ever raising TX to start the T2 reply pulse.
Whoever wrote the component may have been thinking of it the way I originally was: That the receive pulse is the actual sonar echo, arriving sooner if the target is nearer. But in fact it’s just a PWM datum, and its leading-edge arrival time is meaningless, only its duration matters.
It’s satisfying if I analyse and solve a problem and get some electronics working. But it is likewise satisfying to read about you doing it. Love it. Thanks. Good job.
I only wish I could provide a suggested improvement to the component’s code in a way that would fit the processes and customs of today’s software team environment but I can barely use git.
(I’m old-school. Very old-school)
Basically I’d just add a different timeout for waiting across T1, because it’s not the same thing as the distance. It’s just a characteristic of this sensor’s protocol, and is not itself a report datum.
So all that’s needed is to change the ‘timeout’ value used on lines 24 and 26 of ultrasonic_sensor.cpp.
I’d make it something like 30ms, just to give whatever device is used plenty of time to start its report signal. Both those lines are just spinning and waiting for the report signal to begin. It doesn’t get measured until the while on line 29 concludes.
Oh, but I did do the math and ran it through their existing logic. If I had set ‘timeout’ to about 2.4m[etres], that would have made the timeout long enough to span the 14ms T1 period, and it would have worked. But I just brute-forced it with 10.0. Turns out the max reportable distance for this device is about 5.98metres, which corresponds to the 35ms pulse width it sends for “didn’t get an echo.”
I have no doubt that this is likely the solution but I am getting different/interesting results.
First off, when I set the trigger pin to be inverted like your example the output is always NaN but when I set it not inverted it do get a value, but not the value I expected.
I used:
sensor:
- platform: ultrasonic
trigger_pin:
number: D3
inverted: false
echo_pin:
number: D2
name: "Salt Tank Gauge"
update_interval: 5s
timeout: 10.0m
I get in the logs - pointing into the tank (abt 30 cm to the surface):
[20:00:40][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.01 m
[20:00:40][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00635 m with 2 decimals of accuracy
[20:00:45][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:00:45][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00120 m with 2 decimals of accuracy
[20:00:50][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:00:50][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00103 m with 2 decimals of accuracy
[20:00:55][D][ultrasonic.sensor:036]: 'Salt Tank Gauge' - Distance measurement timed out!
[20:00:55][D][sensor:113]: 'Salt Tank Gauge': Sending state nan m with 2 decimals of accuracy
[20:01:00][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:01:00][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00103 m with 2 decimals of accuracy
[20:01:05][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:01:05][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00103 m with 2 decimals of accuracy
[20:01:10][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.01 m
[20:01:10][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00652 m with 2 decimals of accuracy
[20:01:15][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.05 m
[20:01:15][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.05316 m with 2 decimals of accuracy
[20:01:20][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.03 m
[20:01:20][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.02675 m with 2 decimals of accuracy
[20:01:25][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:01:25][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00223 m with 2 decimals of accuracy
and pointing at the ceiling (abt 130 cm away):
[20:03:25][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00120 m with 2 decimals of accuracy
[20:03:30][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:03:30][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00120 m with 2 decimals of accuracy
[20:03:35][D][sensor:113]: 'Salt Tank WiFi Signal': Sending state -30.00000 dBm with 0 decimals of accuracy
[20:03:35][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:03:35][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00103 m with 2 decimals of accuracy
[20:03:40][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:03:40][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00120 m with 2 decimals of accuracy
[20:03:45][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.02 m
[20:03:45][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.02281 m with 2 decimals of accuracy
[20:03:50][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:03:50][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00497 m with 2 decimals of accuracy
[20:03:55][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:03:55][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00120 m with 2 decimals of accuracy
[20:04:00][D][ultrasonic.sensor:040]: 'Salt Tank Gauge' - Got distance: 0.00 m
[20:04:00][D][sensor:113]: 'Salt Tank Gauge': Sending state 0.00120 m with 2 decimals of accuracy
I don’t know, maybe I damaged the sensor while trying some test or something.
I can’t recall what board you’re using. One of the least-ambiguous ways to specify pins is to call them by their GPIOn names. I’m not a fan of using the board labels like ‘D3’, etc, because IMO using GPIOn forms can reduce likelihood of problems.
That said, on a d1_mini D3 maps to GPIO0, which may be pulled up by an LED on some boards.
I suggest trying different GPIO pins, if possible, and D2/D1 (GPIO’s 4&5) would be my preference for this, as those pins are less likely to be burdened by pullups/pulldowns from the board itself.
The GPIO12-14 (D5-7) range are also faves, but GPIOs 4&5 work well too, as long as you don’t have to use them for an I2C bus.
So, don’t write the sensor off until you’ve verified that it’s not your board causing it to misreport.
Oh, and not inverting the trigger pin also ‘worked’ in my testing, but I believe that was only because it only cares about the falling edge, which just meant it was triggering at the end of the trigger pulse instead of the beginning. The pulse is far less than the T1 time, so it shouldn’t matter, unless there’s a pullup/down on that pin. But in my case it gave results either way.
So I’d vacate D3 as the trigger, and maybe try using D5/D6 instead, for example.
glyndon, cannot even begin to thank you enough for your patients and persistence on this issue.
I moved from D2 and D3 and used GPIOx and it appears to be working perfectly! Code working for me (same as yours except pins reversed and trigger pin not inverted):
sensor:
- platform: ultrasonic
trigger_pin:
number: GPIO5
inverted: false
echo_pin:
number: GPIO4
name: "Salt Tank Gauge"
update_interval: 30s
timeout: 10.0m
Lesson learned: Know the dang board I’m working with (D3!=D2 feature-wise)
Thanks again for all your help, talk to you in about 5 months when this one also corrodes and I need to figure out a pressure sensor.
Glad it works now!
You might still want to go ahead and invert the trigger pin so the waveform is what the sensor expects (normally-high, leading-edge is falling). You shouldn’t be able to tell the diff, but it would be a precaution in case they change design to one that insists on it being normally-high and someone copies the config from here someday …
This is great! Thank you for post it.