Tytuł brzmi wspaniale by odwrócić uwagę czytelników od faktu, że chodzi o czujnik poziomu nieczystości w szambie. Zdarzyło mi się całkiem niedawno zapomnieć o konieczności sprawdzenia, czy już nie pora wezwać zaprzyjaźnionego szambelana. O zaniedbaniu dowiedziałem się z nienacka, po wejściu do łazienki w piwnicy, gdzie w obniżeniu podłogi pod prysznicem wdzierała się do domu ciecz, rozsiewając zabójczą woń. Był to piątek wieczór, sytuacja fatalna... Taki ze mnie magik od inteligentnych domów, a mam szambo w łazience. Koniec.
Za 8$ kupiłem 5 czujników HC-SR04 na www.aliexpress.com. Przesyłka doszła w 12 dni. Cena śmieszna, a zapas się przyda, choćby dlatego, że podczas pracy łatwo coś popsuć. Czujniki te są bardzo proste w obsłudze - wystarczy podłączyć je do prądu (5V na nóżki VCC i GND), wysłać impuls na nóżkę Trig i czekać na odpowiedź (impuls) na nóżce Echo. Czas pomiędzy wysłaniem impulsu a jego powrotem po przeliczewniu da zmierzoną odległość.
Podłączenie czujnika do RPi oraz kod w PYTHONie w całości oparłem na pracy Matt'a Hawkins'a z raspberry-spy.co.uk. Jako że zasilamy czujnik napięciem 5V, impuls echo, wracający do odczytu też będzie 5V. To za dużo dla RPi, które akceptuje na GPIO 3,3V. Należy więc napięcie obniżyć przy wykorzystaniu 2 oporników. Oto schemat podłączenia znaleziony na raspberry-spy:
W moim przypadku, jako że podłączam czujnik poprzez replikator GPIO, opisany w oddzielnym artykule, ochroną przed wyższym napięciem zajmują się diody zenera. Opornik R330 jest też w standardzie. Do skorzystania z czujnika HC-SR04 nie potrzebowałem więc nic.
Na obudowę wybrałem Z54JH firmy Kradex. Dodatkowo kupiłem na dziale elektrycznym zwykłe plastikowe rurki fi 18. W obudowie wywierciłem otwory, rurki wkleiłem klejem dwuskładnikowym. Po co w ogóle jakieś rurki? Podczas prób okazało się, że zbliżenie czujnika do pionowej ściany zakłóca odczyt. W moim wypadku szambo jest ok 1,5m pod ziemią, prowadzi do niego studzienka z kręgów. Czujnik jednak nie może być zbyt głęboko, by dało się go podłączyć, wymienić itd. Stąd też od puszki do końca studzienki impulsy biegną w rurkach... Może tak być, że to zupełny bezsens i herezja, ale działa. Odczyt zniekształcony jest o kilka cm, ale przecież nie chodzi mi o precyzyjny pomiar, ale o dane proporcjonalne... Puste szambo da pomiar maksymalny, pełne minimalny... ostatecznie nie są w ogóle potrzebne jednostki.
Ostateczny montaż trwał 2 tygodnie, jako że musiałem ułożyć 12m kabla i przełożyć na całej długości kostkę brukową. Potem, zwiszony do pasa w studzience wierciłem i lutowałem wtyczkę (zrobioną z żeńskiej listwy goldpin-owej). Ostatecznie całość wygląda następująco:
Jak widzicie, rurki przymocowane są zwykłą obejmą stosowaną w montażu instalacji elektrycznych. Rozstaw rurek jest w niej nieco za duży... ale, jak się okazało, brak równoległego ułożenia nie przeszkadza w dokonywaniu pomiaru.
Teraz czas na programowanie. Oto plik do odczytu odległości (us.py):
#!/usr/bin/python #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ #|R|a|s|p|b|e|r|r|y|P|i|-|S|p|y|.|c|o|.|u|k| #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # ultrasonic_1.py # Measure distance using an ultrasonic module # # Author : Matt Hawkins # Date : 09/01/2013 # Import required Python libraries import time import RPi.GPIO as GPIO # Use BCM GPIO references # instead of physical pin numbers GPIO.setmode(GPIO.BCM) # Define GPIO to use on Pi GPIO_TRIGGER = 23 GPIO_ECHO = 24 # Set pins as output and input GPIO.setup(GPIO_TRIGGER,GPIO.OUT) # Trigger GPIO.setup(GPIO_ECHO,GPIO.IN) # Echo # Set trigger to False (Low) GPIO.output(GPIO_TRIGGER, False) # Allow module to settle time.sleep(0.5) # Send 10us pulse to trigger GPIO.output(GPIO_TRIGGER, True) time.sleep(0.00001) GPIO.output(GPIO_TRIGGER, False) start = time.time() while GPIO.input(GPIO_ECHO)==0: start = time.time() while GPIO.input(GPIO_ECHO)==1: stop = time.time() # Calculate pulse length elapsed = stop-start # Distance pulse travelled in that time is time # multiplied by the speed of sound (cm/s) distance = elapsed * 34000 # That was the distance there and back so halve the value distance = distance / 2 print "%.1f" % distance # Reset GPIO settings GPIO.cleanup()
Aby umożliwić wysyłanie danych do PLC przygotowałem plik us.php. Zwraca on odległość pomnożoną przez 10 i oddzieloną średnikiem, by do odczytu można było wykorzystać ten sam kod, który odczytuje temperatury (do znalezienia na dole innego artykułu):
<?php$ level = exec('sudo python /var/python/us.py'); echo $level*10; echo ";"; ?>
W celu wysyłania odczytów czujnika do bazy SQL (sql_us.py):
#!/usr/bin/python import MySQLdb import subprocess from time import localtime, strftime timer = strftime("%Y-%m-%d %H:%M:%S", localtime()) try: process = subprocess.Popen("/var/python/us.py", stdout=subprocess.PIPE) level = float(process.stdout.readline())*10 except: print timer + " : Error reading us distance (us.py)" else: try: db = MySQLdb.connect("yourserver","youruser","yourpassword","yourdatabase") except: print timer + " : Error connecting to the SQL database" else: cursor = db.cursor() try: cursor.execute("INSERT INTO TankLevel(Time, Level) VALUES (%s, %s)", (timer, level)) db.commit() print timer + " : Sending data ok" except: db.rollback() print timer + " : Error executing query" db.close()
Na koniec, aby wysyłanie danych odbywało się automatycznie, w można skonfigurować cron'a (crontab -e):
5 8,20 * * * /var/python/sql_us.py >> /var/python/log/sql_us.log 2>&1