#!/usr/bin/env python # coding: utf-8 # # 9. előadás # *Tartalom:* Raspberry Pi: Python GPIO # # ### RaspberryPi: 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](https://www.youtube.com/watch?v=uXUjwk2-qx4) és # [RPi 3B+](https://www.youtube.com/watch?v=i62xdD4QKtA) # # #### A RPi 3B specifikációja # # - Quad Core 1.2GHz BCM2837 CPU - 64bit # - 1GB SDRAM # - BCM43143 WiFi & Bluetooth Low Energy # - Wi-fi (802.11 b/g/n, 2,4 GHz) # - Bluetooth 4.1 Classic, Bluetooth Low Energy # - 40pin-es kiterjesztett GPIO # - 4 x USB 2.0 port # - 4 pólusú jack a sztereó hang és kompozit videó kimenethez # - HDMI kimenet # - CSI kamera port csatlakozó a Raspberry Pi kamera részére # - DSI Display Port csatlakozó érintőképernyőhöz # - Micro SD kártya csatlakozó az operációs rendszer betöltéséhez és az adatok tárolására # - Javasolt tápegység minimum 1.2A 5V, de maximum 2.5A 5.1V # - Micro USB-s tápcsatlakozás maximum 2.5A 5.1V - nagyobb átfolyó áramtól a csatlakozók, illetve a nyomtatott áramköri hutalozás maradandóan károsodhat, ezért több, vagy nagyobb fogyasztású külső eszköznek érdemes direkt tápellátást biztosítani! # - Mechanikai kompatibilitás a B+ és Pi 2 verziókkal # # #### A RPi 3B+ specifikációja # # - CPU: Broadcom BCM2837B0, Cortex-A53 64-bit SoC @ 1.4GHz - 10%-al gyorsabb a sima Pi 3-nál, meglátjuk majd, hogy mire elég. # - Memória: 1GB LPDDR2 SDRAM - ez változatlan # - Wifi: IEEE 802.11 b/g/n/ac szabványú kétsávos rendszer (2.4 GHz és 5GHz) - ez egy ütős változás! # - LAN: Gigabit Ethernet (Microchip LAN7515), ami jelen esetben max. 300Mbps - ez háromszorosa a korábbi verziókénak, végre!!! # - USB: 4x USB 2.0 - sokan várnák az USB 3-as csatlakozást, de az indokolatlanul drágítaná az eszközt. # - GPIO: a Pi 1 B+ óta megszokott 40-pin, hála a tervezőknek. # - Videó és hang (ez sem módosult): # - HDMI kimenet # - MIPI DSI display port # - MIPI CSI kamera port # - 4 pólusú 3.5mm-es jack aljzaton sztereó hang és kompozit videó kimenet # - Multimédia képességek: H.264, MPEG-4 dekóder (1080p30); H.264 enkóder (1080p30); OpenGL ES 1.1, 2.0 grafikus gyorsítás # - SD kártya támogatás: Micro SD támogatás az operációs rendszer futtatásához és adattároláshoz # - Tápellátási lehetőségek: # - 5Vdc/2.5A (max.) micro USB csatlakozón keresztül # - 5Vdc GPIO tüskéken át # - PoE (Power over Ethernet) lehetőség megfelelő HAT alkalmazásával # - Működési hőmérséklet tartomány: 0-50°C között # # # #### Operációs rendszer # # 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](https://www.raspberrypi.org/downloads/) és telepíthetik azt egy üres SD kártyára. Erről részletesebb leírás [ITT](https://www.raspberrypi.org/documentation/installation/noobs.md) és [ITT](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up) található. # # A NOOBS tartalmazza az alábbi operációs rendszerek telepítését: # - Raspbian # - LibreELEC # - OSMC # - Recalbox # - Lakka # - RISC OS # - Screenly OSE # - Windows 10 IoT Core # - TLXOS # # 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. # # # #### Bootolási módok # # A RPi számos bootolási lehetőséget nyújt: # - Bootflow: Boot sequence description; részletesebben [ITT](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/bootflow.md) # - SD card: SD card boot description; korábban szó volt róla # - USB: USB boot description # - Device boot: Booting as a mass storage device; részletesebben [ITT](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/device.md) # - Host boot: Booting as a USB host; részletesebben [ITT](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/host.md) # - Mass storage boot: Boot from Mass Storage Device (MSD); részletesebben [ITT](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/msd.md) # - Network boot: Boot from ethernet; részletesebben [ITT](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/net.md) # # 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](https://github.com/raspberrypi/firmware/raw/master/boot/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. # #### Python GPIO # # 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ó: # # # ![A Raspberry Pi GPIO kiostása](data/raspberry-pi-gpio-pins.png) # # # 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? # 1. A GPIO.BOARD arra utal, hogy a lábakat a panelen levő sorszámozás alapján fogjuk használni. Ez a sorszámozás úgy néz ki, hogy a bal felső láb az 1-es, a mellette levő a 2-es, alattal evő sorban a 3-4-es, és így tovább lefele haladva. Ld.: # 1 2 # 3 4 # 5 6 # . . # . . # . . # 39 40 # 2. A GPIO.BCM pedig arra utal, hogy a Broadcom SOC channel által elnevezett nevekkel fogunk hivatkozni az egyes lábakra. Ezek azok a nevek, amelyek a fenti képen is szereplnek. Pl. GPIO4, GPIO5, stb. # # 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ó: # # ![Breadboard](data/breadboard-connections.png) # # 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. # # ![LED pólusai](data/led.png) # # 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: # # \begin{equation*} # R = \frac{U}{I} = \frac{3.3V-2.1V}{16mA} = \frac{1.2V}{0.016A} = 75\Omega # \end{equation*} # # 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. # # ![LED kötési rajza](data/led_circuit.png) # # ``` # 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. # # ##### Olvasás a bemenetről # # 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: # # ![Nyomógombos LED villogtatás](data/led_button_circuit.png) # # ``` # 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. # # #### Házi feladat # # Fejlesszék tovább a legelső LED-es feladatot, hogy a LED egyfolytában villogjon 1 mp-s szünetekkel. # # # ## A Sense Hat # # 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: # - hőmérséklet # - páratartalom # - nyomás # - orientáció # # 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()` # # ### Szöveg megjelenítése a LED kijelzőn # # 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: # - scroll_speed: azt mondja meg, milyen gyorsan haladjon át a szöveg a kijelzőn. Default: 0.1 # - text_colour: a szöveg színét definiálja, három szín értékkel: RGB. # - back_colour: ugyanúgy definiálandó, mint a text_colour, de ez a háttér színét határozza meg. # # # #### Szöveg megjelenítése a LED kijelzőn # # 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) # ``` # # #### Egy darab karakter megjelenítése a LED kijelzőn # # 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() # ``` # # ### Kép megjelenítése a LED kijelzőn # # 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ó: # # ![LED mátrix koordinátái](data/coordinates.png) # # 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) # ``` # # ### Az érzékelők használata # # 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: # # \begin{equation*} # T_{kalibrált} = T_{mért} - \frac{T_{cpu} - T_{mért}}{Koefficiens} # \end{equation*} # # ahol 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. # # # ### Orientáció meghatározása # # 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: # - gyorsulásmérő (gyorsulási erőt érzékel) # - giroszkóp (tájolást érzékel) # - magnetométer (a Föld mágneses terét érzékeli) # # Amikor meghívjuk a `get_orientation()` metódust, ez három adatot szolgáltat számunkra: # - pitch: dőlés # - roll: perdülés # - yaw: irányváltoztatás # # A szemléltetés kedvéért az alábbi képen látható a három mozgás tengelye. # # ![3-tengelyes mozgás](data/oreantation.png) # # 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. # # # ### A joystick használata # # Ö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() # ``` # # ______________________________________________________________________________________________________ # # ## _Used sources_ / Felhasznált források # - [Shannon Turner: Python lessons repository](https://github.com/shannonturner/python-lessons) MIT license (c) Shannon Turner 2013-2014 # - [Siki Zoltán: Python mogyoróhéjban](http://www.agt.bme.hu/gis/python/python_oktato.pdf) GNU FDL license (c) Siki Zoltán # - [BME AUT](https://github.com/bmeaut) MIT License Copyright (c) BME AUT 2016-2018 # - [Python Programming](https://pythonprogramming.net) (c) OVER 9000! PythonProgramming.net # - [Raspberry Pi Foundation](https://github.com/RPi-Distro/python-sense-hat) Copyright 2015- Raspberry Pi Foundation