--- /dev/null
+#!/usr/bin/python3
+
+from __future__ import print_function
+from PIL import Image, ImageDraw, ImageFont
+from Adafruit_SSD1306 import SSD1306_128_64
+from RPi import GPIO
+from threading import Thread
+from time import sleep, localtime, strftime, time
+from statistics import median
+
+GPIO.setmode(GPIO.BCM)
+GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)
+GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP)
+button1,button2 = 0, 0
+
+TRIG_PIN = 17
+ECHO_PIN = 27
+
+class RangeSensor():
+ def __init__(self):
+ self.cancelled = False
+ GPIO.setup(TRIG_PIN, GPIO.OUT)
+ GPIO.setup(ECHO_PIN, GPIO.IN)
+ GPIO.output(TRIG_PIN, False) # set low
+
+ def cancel(self):
+ self.cancelled = true
+
+ def measure(self):
+ GPIO.output(TRIG_PIN, False) # ensure a low period before the high pulse
+ sleep(0.000005) # 5us
+ GPIO.output(TRIG_PIN, True)
+ sleep(0.000010) # 10us
+ GPIO.output(TRIG_PIN, False)
+ init = start = stop = time()
+ valid = False
+ while GPIO.input(ECHO_PIN) == 0 and ((start - init) < 0.050) and not self.cancelled:
+ start = time()
+ while GPIO.input(ECHO_PIN) == 1 and ((start - init) < 0.050) and not self.cancelled:
+ valid = True
+ stop = time()
+ if self.cancelled or not valid:
+ return 0,0
+ # max range is 4000mm so 24 ms echo time. Docs recomment 60ms between measurements
+ # to avoid problems with the previous pulse.
+ # note freq is 40kHz and cats can hear up to 60kHz. Short pulse though.
+ elapsed = stop - start
+ distance = elapsed * 340000 # speed of sound in mm/s
+ distance = distance / 2 # its an echo, so halve it.
+ return elapsed,int(distance + 0.5)
+
+class RangeReader(Thread):
+ """Read the distance sensor once every second"""
+ def __init__(self):
+ self.running = False
+ self.value = 0
+ self.values = []
+ self.sensor = RangeSensor()
+ #self.log = open('/var/tmp/sensor.log','a+')
+ Thread.__init__(self)
+
+ @property
+ def distance(self):
+ """Return distance in mm"""
+ return self.value
+
+ def stop(self):
+ self.running = False
+ self.sensor.cancel = True
+
+ def run(self):
+ self.running = True
+ delay = 0
+ while self.running:
+ if delay == 0:
+ self.update()
+ delay = 3
+ else:
+ delay = delay - 1
+ sleep(0.1)
+
+ def update(self):
+ global button1
+ if not button1:
+ elapsed,value = self.sensor.measure()
+ self.values.append((elapsed,value))
+ if len(self.values) == 3:
+ self.value = median([x[1] for x in self.values])
+ #self.log.write(repr(self.values))
+ #self.log.write("\n")
+ self.values = []
+
+def OnButtonPress(channel):
+ global button1, button2
+ if channel == 22:
+ button1 = not button1
+ else:
+ button2 = not button2
+
+def add_button_event(channel):
+ result = False
+ try:
+ GPIO.add_event_detect(channel, GPIO.BOTH,
+ callback=OnButtonPress, bouncetime=300)
+ result = True
+ except RuntimeError as e:
+ pass
+ return result
+
+w1dev = r'28-0000063e0ff4'
+
+class W1Sensor(Thread):
+ """Reading the OneWire sensor is slow so use a worker thread
+ and post the results to the main application at 5 second intervals"""
+
+ def __init__(self, id):
+ self.path = "/sys/bus/w1/devices/%s/w1_slave" % id
+ self.running = False
+ self.t = 0.0
+ Thread.__init__(self)
+
+ @property
+ def temperature(self):
+ return self.t
+
+ def stop(self):
+ self.running = False
+
+ def run(self):
+ self.running = True
+ delay = 0
+ while self.running:
+ if delay == 0:
+ self.update()
+ delay = 50
+ else:
+ delay = delay - 1
+ sleep(0.1)
+
+ def update(self):
+ """Get the temperature from the on-board OneWire sensor"""
+ try:
+ with open(self.path, 'rb') as f:
+ data = f.read().decode('utf-8')
+ t = data.split("\n")[1].split(" ")[9]
+ self.t = float(t[2:]) / 1000.0
+ except Exception as e:
+ print(e)
+ raise
+
+def main(args = None):
+ for chan in [22,23]:
+ count = 0
+ while count < 10:
+ count = count + 1
+ if add_button_event(chan):
+ break
+ if count > 9:
+ print("failed to connect channel %d" % chan)
+
+ d = SSD1306_128_64(i2c_bus=1,rst=0)
+ d.begin()
+ d.clear()
+ d.display()
+
+ #font = ImageFont.load_default()
+ fontpath2 = '/usr/share/fonts/truetype/freefont/FreeMono.ttf'
+ fontpath = '/usr/share/fonts/truetype/roboto/Roboto-Bold.ttf'
+ #fontpath2= '/usr/share/fonts/truetype/roboto/Roboto-Regular.ttf'
+
+ font = ImageFont.truetype(fontpath, 28)
+ font2 = ImageFont.truetype(fontpath, 15)
+
+ buffer = Image.new('1', (d.width, d.height))
+ draw = ImageDraw.Draw(buffer)
+
+ rangeSensor = RangeReader()
+ rangeSensor.start()
+ sensor = W1Sensor(w1dev)
+ sensor.start()
+
+ msg = last_msg = None
+ temp = last_temp = None
+ dist = last_dist = None
+ but = last_but = None
+ try:
+ while True:
+ now = localtime()
+ temp = "%.2f" % sensor.temperature
+ msg = strftime('%H:%M:%S', now)
+ dist = "%d mm" % rangeSensor.distance
+ but = "%d %d" % (button1, button2)
+ if not (temp == last_temp and msg == last_msg and but == last_but and dist == last_dist):
+ last_temp,last_msg,last_but,last_dist = temp,msg,but,dist
+ draw.rectangle((0,0,d.width,d.height), outline=0, fill=0) #erase
+ draw.text((0, 0), temp, font=font2, fill=255)
+ draw.text((90, 0), but, font=font2, fill=255)
+ draw.text((5, 18), msg, font=font, fill=255)
+ if not button1:
+ draw.text((0,48), dist, font=font2, fill=255)
+ d.image(buffer)
+ d.display()
+ sleep(0.1)
+ except KeyboardInterrupt as e:
+ sensor.stop()
+ rangeSensor.stop()
+ pass
+
+if __name__ == '__main__':
+ import sys
+ sys.exit(main())