Tartalom: Raspberry Pi: Python GPIO
A RaspberryPi (továbbiakban: RPi) egy mikroszámítógép. A tantárgy keretein belül a RPi 3 változatát fogjuk tárgyalni. Ennek is két fajtája a létezik, a RPi 3B (régebbi) és a RPi 3B+ (újabb). Az előadás szempontjától nem teszünk lényeges különbséget a kettő között.
A két modell bemutató videója az alábbi linkeken található: RPi 3B és RPi 3B+
Alapvetően a kezdők számára javasolt és megvásárolható egy előre telepített SD kártya, amely tartalmaz egy ún. NOOBS (New Out Of Box System) rendszert. Ez egy telepítő-menedzser, amely könnyebbé teszi az operációs rendszer telepítését. A haladó felhasználók letölthetik a NOOBS-ot INNEN és telepíthetik azt egy üres SD kártyára. Erről részletesebb leírás ITT és ITT található.
A NOOBS tartalmazza az alábbi operációs rendszerek telepítését:
Bár alapvetően a NOOBS csak a Raspbian és LibreELEC rendszerek telepítőcsomagját tartalmazza, a többi rendszer választásakor rendelkeznünk kell internet kapcsolattal, ahonnan letöltődik a kívánt csomag.
A RPi optimális működéshez Raspbian rendszer javasolt, amely egy Debian-alapú Linux. Sajnos a RPi 3B+ nem támogatja az Ubuntu kernelt, így az Ubuntu MATE és Ubuntu Core rendszerek csak a sima RPi 3 és RPi 2 változatokon használhatók.
A RPi számos bootolási lehetőséget nyújt:
A régebbi RPi változatoknál, valamint azokban az esetekben amikor a RPi 3 bootolása valami miatt meghiúsul, újfajta bootolási módszereket vezettek be, ezek az MSD és az Ethernet. Ehhez nem kell mást tenni, mint FAT32 formátumra formázni az SD kártyát és rámásolni a legfrisebb bootcode.bin fájlt. Ez bekapcsolja az új boot módokat, valamint bug fixeket tartalmaz a bootolási meghiúsulások kiküszöbölésére.
A GPIO (General Purpose Input Output) egy általános felhasználású port, ahogy a neve is sugallja, mellyel különféle kimeneti és bemeneti jeleket tudunk manipulálni. A RPi GPIO lábkiosztása az alábbi képen látható:
Látható, hogy csak a zölddel jelölt lábak használhatók teljes mértékben GPIO-ként, a sárgával jelölt lábak csak bizonyos feltétel mellett, és a pirosak egyáltalán nem. Ez utóbbiak főként tápegység kimenetként működnek, melyek értéke általában 3.3V és 5V. Ezeken kívűl a feketével jelölt lábak földellésként viselkednek, jelölésük a GND (Ground).
Ahhoz, hogy használhassuk a RPi GPIO portját, fel kell telepíteni a hozzá tartozó csomagot, melyet az alábbi parancssori parancsok futtatásával tehetünk meg:
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install rpi.gpio
vagy haladóknak sudo pip install RPi.GPIO
Ezután ahhoz, hogy használni tudjuk a GPIO-t, importálni kell a megfelelő python könyvtárat a kódunkba:
import RPi.GPIO as GPIO
Ezt követően inicializálnunk kell a GPIO-t:
GPIO.setmode(GPIO.BOARD)
vagy GPIO.setmode(GPIO.BCM)
Mi a különbség a kettő között?
Mostmár készen állunk a GPIO lábak használatára, akár kimenetként vagy bemenetként, akár valamilyen logikai értékként, stb. Nézzünk néhány konkrét példát:
GPIO.setup(12, GPIO.OUT)
# ez a parancs a 12-es lábat állítja be kimenetként a setup() metódus segítségével
GPIO.output(12, GPIO.HIGH)
# ezzel a 12-es lábat logikai magas szintre állítjuk, ami 3,3V
GPIO.output(12, GPIO.LOW)
# ezzel viszont logikai alacsony szintre állítjuk, ami 0V
Ahhoz, hogy akármilyen szerzort vagy aktuátort vezéreljünk, szükségünk lesz egy ún. "breadboard"-ra. Egy ilyen kellék és a kötési módja az alábbi képen látható:
A középen levő két oszlop lyukai vízszintes irányban képeznek kapcsolatot, míg a szélén levő két vonalban helyezkedő lyukak hosszanti irányba képeznek kapcsolatot, ahogyan be is vannak keretezve.
Nézzünk egy egyszerű LED villogtató példát:
A LED egy kétutas félvezető, amely a rákapcsolt feszültség hatására fényt bocsát ki. Mivel a nevében is szerepel, hogy ez egy dióda, ezért csak egy irányba folyik rajta áram. Figyelni kell tehát a kötésmódra.
Az anódra kerül a pozitív feszültség (3,3V), míg a katódra a földelés (GND). A lábakat úgy tudjuk általában megkülönböztetni, hogy az anód a hosszabb. Továbbá, mivel a GPIO lábak által szolgáltatott 3,3V túl magas a LED-nek, ezért egy ellenállással csökkentjük a LED-re eső feszültséget. A LED-nek 2,1V feszültségre és kb. 20mA áramra van szüksége a működéshez. A GPIO lábak azonban 3,3V feszültséget és csak 16mA áramot szolgáltanak. Nézzük hát a képletet, amivel kiszámolhatjuk a szükséges ellenállás méretét:
R=UI=3.3V−2.1V16mA=1.2V0.016A=75ΩMivel a piaci forgalomban a 75Ω-os ellenállás speciális értékűnek minősül, és általában drágábbak, vagy csak nagyobb tételben vásárolható, ezért számunkra tökéletesen megfelel a sima 82Ω-os, elektronikai szaküzletekben kapható ellenállás.
Miután tehát összekötöttük az áramkört az alábbi képen látható módon, megírhatjuk hozzá a vezérlő Python kódot.
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
GPIO.output(12, GPIO.HIGH)
time.sleep(3)
GPIO.output(12, GPIO.LOW)
GPIO.cleanup()
A gyakorlatban, amikor már nem használjuk tovább a GPIO lábakat, resetelni szoktuk azokat a cleanup()
metódussal.
Hasonlóan, ahogy kimenetként definiáltuk az előbb a GPIO lábat, bemenetként is definiálhatjuk azt:
GPIO.setup(11, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
Látjuk, hogy egy teljesen új pull_up_down
argumentum került bevezetésre, ezt kicsit lentebb magyarázzuk. Az bemenetről való olvasáshoz az input()
metódust használjuk, és ez a GPIO.HIGH
vagy GPIO.LOW
értékeket adja vissza számunkra, attól függően, hogy a bemenetünk aktív, vagy sem.
GPIO.input(11)
Térjünk kicsit vissza a korábban bevezett pull_up_down
argumentumra. Ennek értékeit az alábbi táblázat magyarázza:
Érték | +3.3V | <+3.3V | 0V |
---|---|---|---|
GPIO.PUD_DOWN |
Aktív | Inaktív | Inaktív |
GPIO.PUD_UP |
Inaktív | Aktív | Aktív |
Nézzünk erre is egy példát, az előző feladatot egy nyomógobbal kiegészítve. Akkor fog tehát a LED világítani, ha megnyomjuk a nyomógombot. Nézzük, hogyan fog kinézni ennek a kötési rajza:
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
GPIO.setup(11, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.output(12, GPIO.LOW)
try:
while True:
GPIO.output(12, GPIO.input(11))
except KeyboardInterrupt:
GPIO.cleanup()
Látható a kódból, hogy a 12-es lábra kötött LED akkor lesz GPIO.HIGH
értéken, amikor a GPIO.input(11)
aktívvá válik, vagyis lenyomtuk a nyomógombot.
Fejlesszék tovább a legelső LED-es feladatot, hogy a LED egyfolytában villogjon 1 mp-s szünetekkel.
A Sense Hat egy olyan bővítő modul a RPi-hez, amely különféle szenzorokat, LED-mátrixot és joysticket is tartalmaz. Lássuk milyen szenzorkészlettel rendelkezik:
Ahhoz, hogy kapcsolatunk legyen a Sense Hat-el, be kell illesztenünk a megfelelő könytárat a Python kódunkba. Ez az alábbi sorokkal történik:
from sense_hat import SenseHat
sense = SenseHat()
Ahhoz, hogy valamilyen szöveget - legyen az egy karakter, vagy sztring - megjelenítsünk a LED-es kijelzőn, használhatjuk a show_message("Hello World")
vagy show_letter("A")
függvényeket.
Különféle megjelenítési tulajdonságokat is hozzá adhatunk a kiíró metódushoz, melyeket paramétereknek nevezünk:
Nézzük meg konkrét példán keresztül. Írassuk ki a "Hello World!" szöveget ciklikusan, piros színnel, kék háttéren, és 0.05 átfutási sebességgel.
from sense_hat import SenseHat
sense = SenseHat()
blue = (0, 0, 255)
red = (255, 0, 0)
while True:
sense.show_message("Hello World!", text_colour=red, back_colour=blue, scroll_speed=0.05)
Nézzük meg ezt is példán keresztül. Írassuk ki a Széchenyi Egyetem kezdőbetűit, azaz a SZE betűket, ahol "S" sárga, "Z" fehér, "E" kék, a háttér cián, és a betűk megjelenítési közötti szünet 2 mp. Ehhez szükségünk lesz a time
könyvtár sleep()
függvényére is. A végén pedig a clear()
metódussal töröljük a képernyőt.
from sense_hat import SenseHat
from time import sleep
sense = SenseHat()
cyan = (0, 255, 255)
blue = (0, 0, 255)
white = (255, 255, 255)
yellow = (255, 255, 0)
sense.show_letter("S", yellow, back_colour=cyan)
sleep(2)
sense.show_letter("Z", white, back_colour=cyan)
sleep(2)
sense.show_letter("E", blue, back_colour=cyan)
sleep(2)
sense.clear()
Következőnek generáljunk random színeket és így írassuk ki a "SZE" betűket. Ehhez létrehozunk egy függvényt, valamint szükségünk lesz a randint()
metódusra is.
from sense_hat import SenseHat
from time import sleep
from random import randint
sense = SenseHat()
def pick_random_colour():
random_red = randint(0, 255)
random_green = randint(0, 255)
random_blue = randint(0, 255)
return (random_red, random_green, random_blue)
sense.show_letter("S", pick_random_colour())
sleep(2)
sense.show_letter("Z", pick_random_colour())
sleep(2)
sense.show_letter("E", pick_random_colour())
sleep(2)
sense.clear()
Lehetőség van a kijelzőn képeket is megjeleníteni. Ennek a legprimitívebb változata, amikor egyszínűre fessük a teljes LED mátrixot:
sense.clear((r, g, b))
Továbbá, lehetőségünk van az egyes pixeleket (LEDeket) is befesteni. Minden pixelnek megvan a saját {x,y} koordinátája, amely a bal felső sarokból kezdődik {0,0}-val. Az indexelés az alábbi képen látható:
A pixelek színeit a set_pixel(x, y, colour)
metódussal tudjuk. Nézzünk erre is egy egyszerű kis példát:
from sense_hat import SenseHat
sense = SenseHat()
blue = (0, 0, 255)
red = (255, 0, 0)
sense.set_pixel(0, 2, blue)
sense.set_pixel(7, 4, red)
Egyszerre több pixelt is kezelhetünk a set_pixels
paranccsal, amennyiben egy tömbbe tároljuk el az egyes pixelek értékeit. Nézzük, hogyan:
g = (0, 255, 0) # Green
b = (0, 0, 0) # Black
pixels = [
g, g, g, g, g, g, g, g,
g, g, g, g, g, g, g, g,
g, b, b, g, g, b, b, g,
g, b, b, g, g, b, b, g,
g, g, g, b, b, g, g, g,
g, g, b, b, b, b, g, g,
g, g, b, b, b, b, g, g,
g, g, b, g, g, b, g, g
]
sense.set_pixels(pixels)
A nyomásérzékelőt a sense.get_pressure()
metódussal tudjuk lekérdezni.
from sense_hat import SenseHat
sense = SenseHat()
sense.clear()
pressure = sense.get_pressure()
print(pressure)
Output: 1012.97997042
A páratartalmat a get_humidity()
metódussal tudjuk lekérdezni.
from sense_hat import SenseHat
sense = SenseHat()
sense.clear()
humidity = sense.get_humidity()
print(humidity)
Output: 45.2721951219
A hőmérsékletet alapesetben a get_temperature()
metódussal kérdezzük le.
from sense_hat import SenseHat
sense = SenseHat()
sense.clear()
temp = sense.get_temperature()
print(temp)
Output: 19.9493634245
Itt viszont vigyázni kell a RPi processzor melegedéséből fakadó hibára. Mivel a processzor felmelegíti a szenzorokat is, főleg ha dobozban helyezkednek, ezért kalibrációt kell végezni a pontos hőmérséklet mérés érdekében.
Ennek a képlete az alábbi:
Tkalibrált=Tmért−Tcpu−TmértKoefficiensahol a Koefficienst külső mérésekre alapozva kell meghatározni úgy, hogy a kalibrált érték a legközelebb álljon a környezet valós hőmérsékletéhez.
A mozgások meghatározására a Sense Hat rendelkezik egy ún. IMU-val (Innertial Measurement Unit), vagyis innerciális mérés-egységgel. Az ilyen egységek általában háromféle szenzorból állnak:
Amikor meghívjuk a get_orientation()
metódust, ez három adatot szolgáltat számunkra:
A szemléltetés kedvéért az alábbi képen látható a három mozgás tengelye.
Nézzük meg egy példán keresztül, milyen adatokat kapunk.
from sense_hat import SenseHat
sense = SenseHat()
sense.clear()
o = sense.get_orientation()
pitch = o["pitch"]
roll = o["roll"]
yaw = o["yaw"]
print("pitch {0} roll {1} yaw {2}".format(pitch, roll, yaw))
Output: pitch 356.35723002363454 roll 303.4986602798494 yaw 339.19880231669873
A get_accelerometer_raw()
metódus segítségével nyers adatokat kaphatunk a gravitációs erőnk hatásáról az egyes tengelyekre (x, y, z) nézve. Ha pl. bármelyik tengelyen a gravitációs erőnk ±1G, akkor tudjuk, hogy ez a tengely lefelé mutat. A következő példában az összes tengelyre nézve lekérdezzük a gravitációs gyorsulást, majd a legközelebbi egész számra kerekítjük.
from sense_hat import SenseHat
sense = SenseHat()
while True:
acceleration = sense.get_accelerometer_raw()
x = acceleration['x']
y = acceleration['y']
z = acceleration['z']
x=round(x, 0)
y=round(y, 0)
z=round(z, 0)
print("x={0}, y={1}, z={2}".format(x, y, z))
Vízszintes tengelyen elforgatva a Sense Hatet, azt látjuk, hogy a "x" és "y" tengelyek értékei {-1, 1} között változnak. Ha pedig függőleges irányban forgatjuk el egy π (180°) értékkel, akkor a "z" tengely értéke fog {1, -1} között változni.
Öt féle irányba használhatjuk a Sense Hat joystickjét, lefele (lenyomni), előre, hátra, jobbra, balra. Ezen felül érzékelhetjük a lenyomást, a lenyomva tartást és a felengedést is. A következő példában írassuk ki a joystick éppen aktuális állapotát.
from sense_hat import SenseHat
sense = SenseHat()
while True:
for event in sense.stick.get_events():
print(event.direction, event.action)
Erre a joystick mozgatása közben valami hasonlót kell kapnunk:
('up', 'pressed') ('up', 'released') ('down', 'pressed') ('down', 'released') ('left', 'pressed') ('left', 'released') ('right', 'pressed') ('right', 'released') ('middle', 'pressed') ('middle', 'released')
Készítsünk egy olyan programot, amely a megnyomott irányoktól függően kiírja a LED mátrixra a megfelelő irány kezdőbetűjét.
from sense_hat import SenseHat
from time import sleep
sense = SenseHat()
while True:
for event in sense.stick.get_events():
if event.action == "pressed":
if event.direction == "up":
sense.show_letter("F") # Fel
elif event.direction == "down":
sense.show_letter("L") # Le
elif event.direction == "left":
sense.show_letter("B") # Bal
elif event.direction == "right":
sense.show_letter("J") # Jobb
elif event.direction == "middle":
sense.show_letter("K") # Közép
sleep(0.5)
sense.clear()