[PYTHON] Accurately measure the pulse width of the ultrasonic distance sensor "HC-SR04" using the pigpio callback.

Introduction

Raspberry Pi is generally running on OS (Raspbian, Raspberry Pi OS), so it is not good at accurate pulse generation and pulse width measurement compared to controllers such as Arduino and ESP. I recently learned that by using pigpiod's service, it is possible to generate and measure pulses with reasonable accuracy, if not perfect.

This time, pigpiod conducted an experiment of distance measurement with an ultrasonic distance sensor (HC-SR04) using the callback function prepared as a library for python, so I will upload it.

IMG_20200729_232437.jpg

Environment used

Three I2C temperature, humidity, and barometric pressure sensors are connected at the bottom of the photo, but they are not directly related to this article. By the way, it is very convenient to use node-RED to integrate various sensor results and subsequent processing.

program

This program measures the distance only once when it is started and ends it. The intention is to make the overall integrated state easier to see by leaving all loop processing and periodic startup processing to the above-mentioned node-RED. For reference, the flow of node-RED used this time is posted at the end of the sentence.

I don't see many articles about pigpio callbacks, but the pigpiod author's page (here) describes how to use each function. Since it has been done, I tried to create it while referring to it.

Initialization

Import the pigpio library, set the trigger pin of HC-SR04 and the assignment of the echo pin to GPIO, and set the trigger pin to the output mode and the echo pin to the input mode. Clear t_rise and t_fall for pulse width measurement here.

Callback function definition

This is the liver. This function is called when the callback condition is satisfied. At that time, which GPIO pin is changed, the signal level after the change is H or L, and when is the change timing? It is called including the three information. Regarding the timing tick, the system manages it with a 32-bit variable in units of microseconds, but since there are only 32 bits, it wraps in 2 ^ 32 microseconds (about 72 minutes). Since it seems that this tick cannot be cleared, the time width calculation when wrapping is calculated as exception handling (actually, since the experiment here has not been done, there may be a bug ...)

When the callback is called, if it is H level, it is considered as a rising edge, and the tick at that timing is memorized. When the callback is called, if it is at the L level, it is considered to be a falling edge, and the pulse width is calculated from the tick value from rising to falling. The difference between tick values becomes the value of microseconds as it is (separate calculation is required at the time of the above lap)

To the last, it is decided as a time measurement from the rise to the fall, but since it is a code of HC-SR04 fixed, there is no problem. Strictly speaking, there is still the possibility of malfunction.

Convert the obtained time to distance. Sound propagates 340m per second, so convert the obtained time (microsec) to distance (cm) and calculate half the distance (measured distance is reflected after the trigger appears. Because it is the time to come back and enter echo, it will be a round trip distance)

The result converted to distance is printed to stdout as a character string in json format.

Main processing

Defines how to call the callback function. Here, "echo pin", "detect both rising and falling edges", and "function name to be called at the time of detection" are defined. I'm assigning it to a variable called cb, but I'm not using it. There should be a use for it ...

The following pi.gpio_trigger (HC_SR04_trig, 10, 1) is the part that actually outputs the trigger pulse. It can output "positive" pulses with a width of 10usec. It can output 10usec pulses fairly accurately.

After issuing the trigger, the echo pulse is input, the callback function is called, and the pulse width is measured, but the processing time for that is put to sleep and waited. Without this sleep, there is no time to call callback, and the program will end and the correct operation will not be performed. Considering the measurement range of HC-SR04, if you wait 100msec, the echo signal will surely return, which is considered sufficient.

End processing

Terminate the pigpoid handler called pi that was initialized first. If you don't do this, you may run the risk of not working properly over and over again (although I'm not sure what side effects it actually has if you don't stop ()).

code

Below is the code. If you start this code from the terminal of Raspberry Pi, it will measure once and finish. Also, if you call from node-red, it will be measured once and finished in the same way. The reason why I didn't use loop processing is because I'm thinking about the whole thing in combination with node-red.

sr04.py


# one shot measurement only
#
import pigpio
import time

HC_SR04_trig = 5
HC_SR04_echo = 6

pi = pigpio.pi()
pi.set_mode(HC_SR04_trig, pigpio.OUTPUT)
pi.set_mode(HC_SR04_echo, pigpio.INPUT)

t_rise = 0
t_fall = 0


def cbf(gpio, level, tick):  # call back function for pulse detect _/~~\__
    global t_rise, t_fall

    if (level == 1):  # right after the rising edge
        t_rise = tick
    else:            # right after the falling edge
        t_fall = tick
        if (t_fall >= t_rise):  # if wrapped 32bit value,
            timepassed = t_fall - t_rise
        else:
            timepassed = t_fall + (0xffffffff + 1 - t_rise)

        # meter to cm, microseconds to seconds, divide by 2
        d = 340 * 100 * timepassed / 1000000 / 2
        print('{"tick":%10d, "time_us": %6d, "distance_cm": %.2f}' % (
            tick, timepassed, d))


cb = pi.callback(HC_SR04_echo, pigpio.EITHER_EDGE, cbf)
pi.gpio_trigger(HC_SR04_trig, 10, 1)  # Trig (10μs pulse)
time.sleep(0.1)  # wait for echo signal for 100msec (enough..., I believe...)

pi.stop()

in conclusion

I thought that accurate pulse generation and pulse measurement with Raspberry Pi was not realistic, but thanks to the wonderful service (daemon) called pigpiod and the function group, I was able to experience what I could achieve. Was a good surprise. Servo motor control can also output a level of PWM that can be perfectly stable, and long infrared waveforms such as air conditioner remote controls can be detected without problems, and can also be generated and output.

The interface library for C is also substantial, and I will play with this service for a while. We will not use the gpio palette that is commonly used for node-RED in the future, and plan to integrate it into pigpiod as much as possible.

In this distance measurement, the distance was calculated with a constant of 340 m / sec, but in reality this value has a temperature coefficient, so by correcting it based on the temperature measured by the temperature sensor. , I think you will need a more accurate distance. When I have time, I'll look it up and give it a try.

flow in node-RED

Including sensors other than HC-SR04 (BME280, SHT31, HDC1080), node-RED manages the whole. It's not a big flow, but for your reference. The sr04.py experimented this time is executed once every 10 seconds from the exec node at the bottom of the figure, and only displays the result as a json object in the debug window.

image.png

flow.json


[{"id":"a03baba4.3f9348","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"32a6ead2.729856","type":"pi-gpiod out","z":"a03baba4.3f9348","name":"","host":"localhost","port":8888,"pin":"4","set":"","level":"0","out":"out","sermin":"1000","sermax":"2000","x":530,"y":100,"wires":[]},{"id":"5d96dabf.e6b294","type":"inject","z":"a03baba4.3f9348","name":"1","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":210,"y":100,"wires":[["3930e4bb.0947fc"]]},{"id":"3930e4bb.0947fc","type":"function","z":"a03baba4.3f9348","name":"","func":"msg.payload ^= 1;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":370,"y":100,"wires":[["30c37801.e427a8","32a6ead2.729856"]]},{"id":"30c37801.e427a8","type":"delay","z":"a03baba4.3f9348","name":"500ms","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":360,"y":60,"wires":[["3930e4bb.0947fc"]]},{"id":"50c0eb9.848b614","type":"Bme280","z":"a03baba4.3f9348","name":"","bus":"1","address":"0x76","topic":"bme280","extra":false,"x":540,"y":240,"wires":[["4e3270f3.bf042","3d52960e.1f174a","7648f6d9.71abb8"]]},{"id":"1cd64330.b2bb3d","type":"inject","z":"a03baba4.3f9348","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"10","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":210,"y":240,"wires":[["50c0eb9.848b614","9590ccfc.c5ac9","61958cf7.9b2954","53267a93.8f0324"]]},{"id":"4e3270f3.bf042","type":"debug","z":"a03baba4.3f9348","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":810,"y":240,"wires":[]},{"id":"9590ccfc.c5ac9","type":"exec","z":"a03baba4.3f9348","command":"python3 -u /home/pi/Public/python/sht/sht31.py","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"SHT31","x":400,"y":340,"wires":[["f3d35b8e.0553f8"],[],[]]},{"id":"f3d35b8e.0553f8","type":"json","z":"a03baba4.3f9348","name":"","property":"payload","action":"obj","pretty":false,"x":550,"y":320,"wires":[["4e3270f3.bf042","9edc8a14.d3fb38","b34c115.020e2f"]]},{"id":"61958cf7.9b2954","type":"exec","z":"a03baba4.3f9348","command":"python3 -u /home/pi/Public/python/hdc/hdc1080.py","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"HDC1080","x":400,"y":460,"wires":[["a274fdc4.2ebc9"],[],[]]},{"id":"a274fdc4.2ebc9","type":"json","z":"a03baba4.3f9348","name":"","property":"payload","action":"obj","pretty":false,"x":550,"y":440,"wires":[["4e3270f3.bf042","45f88bc0.caf3d4","4079567a.ca44b8"]]},{"id":"35c5e80b.64be18","type":"ui_chart","z":"a03baba4.3f9348","name":"temperature","group":"2e025274.2bd5de","order":1,"width":12,"height":7,"label":"temperature","chartType":"line","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"3","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":1050,"y":360,"wires":[[]]},{"id":"3d52960e.1f174a","type":"change","z":"a03baba4.3f9348","name":"temperature","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.temperature_C","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"BME280","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":320,"wires":[["35c5e80b.64be18"]]},{"id":"9edc8a14.d3fb38","type":"change","z":"a03baba4.3f9348","name":"temperature","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.temperature","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"SHT31","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":360,"wires":[["35c5e80b.64be18"]]},{"id":"45f88bc0.caf3d4","type":"change","z":"a03baba4.3f9348","name":"temperature","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.temperature","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"HDC1080","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":400,"wires":[["35c5e80b.64be18"]]},{"id":"7648f6d9.71abb8","type":"change","z":"a03baba4.3f9348","name":"Humidity","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.humidity","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"BME280","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":460,"wires":[["dd1a5041.7ed54"]]},{"id":"b34c115.020e2f","type":"change","z":"a03baba4.3f9348","name":"Humidity","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.humidity","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"SHT31","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":500,"wires":[["dd1a5041.7ed54"]]},{"id":"4079567a.ca44b8","type":"change","z":"a03baba4.3f9348","name":"Humidity","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.humidity","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"HDC1080","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":540,"wires":[["dd1a5041.7ed54"]]},{"id":"dd1a5041.7ed54","type":"ui_chart","z":"a03baba4.3f9348","name":"Humidity","group":"2e025274.2bd5de","order":1,"width":12,"height":7,"label":"Humidity","chartType":"line","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"3","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":1050,"y":500,"wires":[[]]},{"id":"53267a93.8f0324","type":"exec","z":"a03baba4.3f9348","command":"python3 -u /home/pi/Public/python/sr04/sr04.py","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"HC-SR04","x":400,"y":640,"wires":[["ae4a3b00.587498"],[],[]]},{"id":"203bb731.6e7e58","type":"debug","z":"a03baba4.3f9348","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":810,"y":620,"wires":[]},{"id":"ae4a3b00.587498","type":"json","z":"a03baba4.3f9348","name":"","property":"payload","action":"obj","pretty":false,"x":550,"y":620,"wires":[["203bb731.6e7e58"]]},{"id":"2e025274.2bd5de","type":"ui_group","z":"","name":"Default","tab":"c8b4aecd.15eec","order":1,"disp":true,"width":12,"collapse":false},{"id":"c8b4aecd.15eec","type":"ui_tab","z":"","name":"home","icon":"dashboard","disabled":false,"hidden":false}]

Articles that I used as a reference

Thank you very much.

Recommended Posts

Accurately measure the pulse width of the ultrasonic distance sensor "HC-SR04" using the pigpio callback.