10
\$\begingroup\$

I am currently developing a touchless lightswitch via a Raspberry Pi, an ultrasonic module and a nice radio module.

However, I am quite a beginner in Python and it was a hard time (at least for me) to accomplish my goal. Whenever the measured distance is falling below the limit the switch is triggered "on" (1) and after that if the distance is falling short again the switch is triggered "off" (0).

#!/usr/bin/env python
import os
import sys
import time
import RPi.GPIO as GPIO
GPIOTrigger = 23
GPIOEcho = 24
def wait(sec):
 while sec > 0:
 sys.stdout.write(str(sec) + ' \r')
 sec -= 1
 time.sleep(1)
def MeasureDistance():
 GPIO.output(GPIOTrigger, True)
 time.sleep(0.00001)
 GPIO.output(GPIOTrigger, False)
 StartTime = time.time()
 while GPIO.input(GPIOEcho) == 0:
 StartTime = time.time()
 while GPIO.input(GPIOEcho) == 1:
 StopTime = time.time()
 TimeElapsed = StopTime - StartTime
 Distance = (TimeElapsed * 34300) / 2
 return Distance
def main():
 STATUS = 0
 try:
 while True:
 Distance = MeasureDistance()
 if Distance > 10.0:
 time.sleep(0.01)
 else:
 if STATUS != 1:
 print("Passing by (%.1f cm)" % Distance)
 import subprocess
 subprocess.call(["/root/rcswitch-pi/send", "10000", "1", "1"])
 wait(5)
 STATUS = 1
 else:
 subprocess.call(["/root/rcswitch-pi/send", "10000", "1", "0"])
 wait(5)
 STATUS = 0
 except KeyboardInterrupt:
 print("Stopped by user")
 GPIO.cleanup()
if __name__ == '__main__':
 # use GPIO pin numbering convention
 GPIO.setmode(GPIO.BCM)
 GPIO.setup(GPIOTrigger, GPIO.OUT)
 GPIO.setup(GPIOEcho, GPIO.IN)
 GPIO.output(GPIOTrigger, False)
 main()

This works but I am unsure whether there is a nicer way of determining the last status of STATUS or if there are some pitfalls I might not see.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Apr 24, 2014 at 19:22
\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

MeasureDistance. The way it is written, sometimes it measures the duration of the signal, sometimes the (desired) time for the echo to arrive (consider what happens if you enter the first while loop). Suggestion:

def MeasureDistance():
 StartTime = time.time()
 GPIO.output(GPIOTrigger, True)
 while GPIO.input(GPIOEcho) == 0:
 pass
 # Found the raising front
 StopTime = time.time()
 GPIO.output(GPIOTrigger, False)

Of course, if the echo arrives faster than the code executes (and Python is slow), you'd get the elapsed time 0. I suppose there's nothing you can do about that, other than resorting to interrupts. Also, you may want to put some safeguards (a timer maybe) in case the raising front never arrives.

main. I don't see the need to import subprocess in the middle of the code. Also, state variables tend to create an unmaintainable code. Suggestion:

 try:
 Distance = MeasureDistance()
 while True:
 while Distance > 10.0:
 Distance = MeasureDistance()
 wait(5)
 switch_on()
 while Distance < 10.0:
 Distance = MeasureDistance()
 wait(5)
 switch_off()
 except KeyboardInterrupt:

PS: a magic 34300 constant must go. Give it a nice symbolic name (yes we all know it has something to do with the speed of sound).

Update. Usually in the situations like yours you may have a funny behaviour at the breaking distance. Due to random factors (like the code execution time, and many more others) the distance is measured with a certain error, and the switch will go on and off unpredictably. To deal with it, allow for a hysteresis:

 while Distance > breaking_distance - tolerance:
 Distance = MeasureDistance()
 wait(5)
 switch_on()
 while Distance < breaking_distance + tolerance:
 Distance = MeasureDistance()
 wait(5)
 switch_off()
answered Apr 24, 2014 at 23:58
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.