Nutimaja ehitamine: tarkvara

Järgnev blogipostitus on jätk nutimaja ehitamise sarjale. Kui aga eelnev peatükk riistvara ehitamise teemal jäi mingil põhjusel vahele, siis saab seda lugeda siin.

Sisukord

  1. Programmeerimiskeskkonna ülesseadmine
  2. Esimesed sammud universaalportidega
  3. Liikumise peale valgusti juhtimine
  4. Valgusti juhtimine mõnest teisest seadmest (nt nutitelefonist)
  5. Kuhu edasi?

Töövahendid

  1. Raspberry Pi 3B+*
  2. SD mälukaart
  3. HDMI kaabel
  4. Hiir
  5. Klaviatuur
  6. Monitor HDMI toega
  7. Kohtvõrk
  8. Etherneti kaabel (valikuline)

* Tegelikult sobib ükskõik milline versioon. Konkreetse ettevõtmise jaoks võivad erinevuseks olla GPIO viikude asukohad ja numbrid, mistõttu tuleks ühendamisel üle kontrollida vastavad tähistused paigutusplaanist (pin layout).

Eeltöö

Selleks, et alustada Raspberry Pi peal programmeerimisega, tuleb üles seada vastav keskkond. Kuna aga Raspberry Pi sarnaneb oma omadustelt pigem miniarvutiga, siis alustame lisakomponentide (monitor, klaviatuur, hiir) ühendamisega ning operatsioonisüsteemi paigaldamisega. Kõige lihtsam on seda teha tootja poolse step by step juhendi järgi, mille leiab siin.

Et olla kindlalt veendunud, et kõik vajalikud tööriistad meie projekti jaoks oleksid olemas, siis soovitame käsureale (mille saab avada klõpsates Menu->Accessories->Terminal või üle SSH) anda järgmise ülesande:

sudo apt-get -y install python python-dev python-rpi.gpio

Esimesed sammud universaalportidega (GPIO)

Meie esimeseks ülesandeks on luua programm, mis vilgutaks eelmises blogipostituses paigaldatud leedi sisse-välja. Lisaks meie kirjutatud koodijuppidele, saab Python kohta rohkem uudistada W3Schools tasuta õppekeskkonnas.

Igasugune programmeerimistöö algab alati faili tekitamisega, mille saab luua käsurealt järgmiselt:

nano led_vilgutus.py

Seejärel kopeerime aknasse järgneva sisu:

# coding=utf-8 # ütleme programmile, kuidas on tekst esitletud

# "#" tähemärk viitab kommentaari algusele, mis omakorda tähendab, sellele järgnev tekst kuni
# uue rea alguseni ei ole mõeldud nö arvutile täitmiseks, vaid lisatakse programmeerijate koodilugemise
# ja -kirjutamise lihtsustamiseks. Kusjuures sellele tehakse erand, kui esimese kahel real
# on defineeritud mõni teksitvormigu (i.k. encoding) reegel nagu on seda tehtud ka meie näites.

import RPi.GPIO as GPIO     # ütleme programmile, et soovime universaalportide käske kasutada
import time                 # ütleme programmile, et soovime ajaga seotud funktsioone kasutada

LED = 16                    # loome muutuja LED, mille väärtus on 16, edaspidi programm teab,
                            # et sõna "LED" tähendab arvu 16

GPIO.setmode(GPIO.BCM)      # soovime kasutada universaalporte numbrite järgi
GPIO.setup(LED, GPIO.OUT)   # soovime 16 GPIO viiku kasutada väljundina

while True:                 # tahame tsüklit, et programm kestaks pidevalt
GPIO.output(LED, GPIO.HIGH) # anname pinge 16ndale (ehk LED) viigule
print("Led ON")             # kirjutame vastave teksti terminali vaatamiseks
time.sleep(1)               # puhkame 1 sekundi, programm puhkamise ajal midagi ei tee
GPIO.output(LED, GPIO.LOW)  # võtame pinge ära 16ndalt viigult
print("Led OFF")
time.sleep(1)

Tekstiredaktorist väljumiseks tuleb kasutada klahvikombinatsiooni Cntr+X ning kui programm avaldab soovi salvestamise järele (“Save modified buffer?“), siis vajutage klahve “Y” ja Enter.

Programmi käivitamiseks tuleb anda käsklus:

python led_vilgutus.py

Sulgemiseks tuleb vajutada kombinatsiooni Cntr+C.

NB! Kui peaks juhtuma, et mingil põhjusel leed ei vilgu, siis esimesena kontrollige üle universaalpordi number. Seda, kas programm reaalselt pinget väljastab, saab valideerida multimeetri alalispinge mõõtmise režiimis, ühendades selle klemmid leedi “jalgade” külge.

Liikumise peale valgusti juhtimine

Siinkohal võtame korra hoo maha ja mõtleme paremaks arusaamiseks läbi, milline peaks olema programmi loogiline töökäik:

  • Kindlasti peaksime alati programmile sarnaselt eelnenud ülesandele, milliseid (viigu number) ja kuidas (kas lugeda või tekitada pinget) me universaalportide viike kasutada tahame
  • Kuna PIR andur tekitab oma keskmise väljundviigu peale liikumise korral pinge, siis peaksime sealt tulevat võimalikku signaali pidevalt lugema
  • Kui dekteerime pinge, siis lülitamine leedi põlema ning selle puudumisel kustutame

Loome uue faili:

nano led_automaatika.py

… ja kopeerimine sinna järgneva sisu:

# coding=utf-8  

import RPi.GPIO as GPIO                 # ütleme programmile, et soovime universaalportide käske kasutada
import time                             # ütleme programmile, et soovime ajaga seotud funktsioone kasutada

LED = 16                                # loome muutuja LED, mille väärtus on 16, edaspidi programm teab,
                                        # et sõna "LED" tähendab arvu 16

PIR_OUTPUT = 18                         # loome muutuja analoogselt LED'ile tähistamaks GPIO viigu numbrit,
                                        # millele annab PIR sensori liikumise korral pinge


GPIO.setmode(GPIO.BCM)                          # soovime kasutada universaalporte numbrite järgi
GPIO.setup(LED, GPIO.OUT)                       # soovime 16 GPIO viiku (LED) kasutada väljundina
GPIO.setup(PIR_OUTPUT, GPIO.IN)                 # soovime 18 GPIO viiku (PIR väljund) lugeda 
                                                # ehk kasutada sisendina

while True:                                     # tahame tsüklit, et programm kestaks pideval

        if GPIO.input(PIR_OUTPUT) == True:      # loeme, kas PIR keskmisel viigul on pinge
                                                # kui on, siis..
                GPIO.output(LED, GPIO.HIGH)     # anname pinge 16ndale (ehk LED) viigule, et tekiks valgus

        else:                                   # muul juhul (kui pinget ei olnud), siis ..
                GPIO.output(LED, GPIO.LOW)      # võtame pinge ära leedilt, sest liikumist ei ole

Seejärel kasutame juba eelnevalt õpitud salvestamisetehnikat ning käivitame programmi:

python led_automaatika.py

NB! Kui peaks juhtuma, et mingil põhjusel leed ei hakka liikumise korral põlema, siis esimesena kontrollige üle universaalportide numbrid. Pingesignaalide olemasolu saab valideerida multimeetri alalispinge mõõtmise režiimis.

Valgusti juhtimine mõnest teisest seadmest

Nagu juba asjade interneti tutvustavas blogiposituses vihjasime, tuleb kaugjuhtimiseks luua kahe seadme vaheline ühendus. Antud postituse raames oleva Raspberry Pi 3 puhul saab kasutada nii kaablipõhist (ethernet) kui ka kaablita (WiFi) lähenemist kohtvõrku ühendamiseks. Esimese puhul piisab sellest, kui ühendame seadme etherneti kaabli abil koduruuteri külge ning WiFi seadistamiseks leiab asjakohase juhendi siit.

Üks lihtsamaid (ja seega ka ebaturvalisem lähenemine) viise kaugjuhtimiseks on kasutada internetibrauserit. Antud projekti raames loome kaks linki, mille kaudu leedi juhtimine toimuma hakkab vastavalt:

  • http:///led_on   <- sisselülitamiseks
  • http:///led_off  <- väljalülitamiseks

Et suvaline veebibrauseri toega samas kohtvõrgus olev seade saaks valgusti juhtimise käsku edastada, peab meie loodav programm olema võimeline kuulama sissetulevaid päringuid. Seda ülesannet oskab lahendada HTTP teenus. HTTP (HyperText Transfer Protocol) on veebimaailmas kasutusel olev reeglite kogum, mis määrab ära, kuidas peaksid veebibrauserid ja veebilehekülgi hoiustavad arvutid (või seadmed) omavahel suhtlema. Selliselt ülesehitusega programmid on asjade interneti maailmas tavaline nähtus. Lahenduse peamiseks eeliseks on lai ühilduvus, mis võimaldab rakendusi lihtsasti programmeerida ja kasutada erinevates seadmetes. Tavaliselt on valmistoodete (näiteks mõne kodujuhtimise äppi puhul) sellised päringud realiseeritud nupuvajutusel nö “tagataustal”.

Antud juhul saab kasutada leedijuhtimiseks järgnevat koodi:

# coding=utf-8


from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer # ütleme programmile, et soovime kasutada 
                                                              # HTTP teenust
import RPi.GPIO as GPIO                 # ütleme programmile, et soovime universaalportide käske kasutada

LED = 16                                # loome muutuja LED, mille väärtus on 16, edaspidi programm teab,
                                        # et sõna "LED" tähendab arvu 16

GPIO.setmode(GPIO.BCM)                  # soovime kasutada universaalporte numbrite järgi
GPIO.setup(LED, GPIO.OUT)               # soovime 16 GPIO viiku kasutada väljundina

class Handler(BaseHTTPRequestHandler):     # ütleme programmile, et soovime ise määrata, mis juhtub päringutel
    def do_GET(self):                       # ütleme, mida tuleb teha, kui brauser ühendub meie serveri külge
        if self.path == "/led_on":          # mida teha, kui URL on /led_on lõpuga 
            GPIO.output(LED, GPIO.HIGH)     # anname pinge 16ndale (ehk LED) viigule
            print("LED ON")
        elif self.path == "/led_off":       # mida teha, kui URL on /led_off lõpuga 
            GPIO.output(LED, GPIO.LOW)      # võtame pinge ära 16ndalt viigult
            print("LED OFF")
        self.send_response(200)             # ütleme brauserile, et võtsime päringu vastu, just seda
                                            # tähistabki kood 200

server_parameters = ('0.0.0.0', 80)              # päringuid võtame vastu Raspberry PI ip aadressil ja pordil 80
httpd = HTTPServer(server_parameters, Handler)  # ütleme HTTP teenusele, mis on parameetrid ja kuidas käituda 
                                                 # päringute korral
httpd.serve_forever()                            # paneme HTTP teenuse käima

Et aga mujalt seadmelt ligi saada, peame teadma meie Raspberry Pi IP aadressi. Selle teadasaamiseks saab kasutada terminal käsku:

hostname -I

Peale programmi käivitamist peaksid olema meie poolt soovitud veebiaadressid kättesaadavad ning nüüd proovimegi samas kohtvõrgus olevast seadmest neid kontrollida. Kuna meie puhul osutus Raspberry IP aadressiks 192.168.0.101, siis leedi juhtimiseks on kasutuses vastavad URL’id:

  • http://192.168.0.101/led_on   <- sisselülitamiseks
  • http://192.168.0.101/led_off  <- väljalülitamiseks

NB! Kui mingil põhjusel peaks programm väljastama veateate “socket.error: [Errno 98] Address already in use”, siis sellega üritab operatatsioonisüsteem öelda, et mingi protsess (programm) juba kasutab porti 80. Sellisel juhul võib segava programmi sulgemiseks kasutada käsku:

kill -9 $(lsof -t -i:80)
Iseseisvaks katsetamiseks

  • Muutke iseseisvalt kasutatavaid universaalporte vastavalt Raspberry Pi paigaldusplaanile
  • Lisage elektriskeemi veel üks leedvalgusti ning vilgutage mõlemat kordamööda
  • Ühendada elektriskeem nii, et leed põleks ainult siis, kui liikumisandur tuvastab liikumise

Blogi valmimist toetavad Haridus- ja Teadusministeerium ning SA Eesti Teadusagentuur.