Software - MaksymOnGit/ambiPi GitHub Wiki
picamera
RPLCD
gpiozero.Button
rpi_ws281x
PyAccessPoint
A többi könyvtár vagy nagyon alap vagy a fentebb felsorolt könyvtárak függőségei.
A kép elemzés a picamera
PiRGBAnalysis
osztályán keresztül történik. Az osztályból egy saját osztályt kell származtatni és felül kell írni az analyze(self, data)
metódusát.
class FrameProcessor(PiRGBAnalysis):
def __init__(self, camera):
super(FrameProcessor, self).__init__(camera)
def analyze(self, data):
#Elemzési algoritmus itt
Majd ebből az osztályból készített objektumot át kell adni a camera objektum start_recording
metódusának.
with PiCamera(resolution=(1920,1088)) as camera:
camera.start_recording(FrameProcessor(camera), format='rgb')
Indítást követőn az analyze
metódus első paraméterében egy 3 dimenziós numpy mátrixot kapunk amiben az első két dimenzió az Y és X koordinátákat jelöli a harmadik pedig nullától kettőig az RGB színösszetevőt.
r=int(data[y, x, 0])
g=int(data[y, x, 1])
b=int(data[y, x, 2])
Itt figyelni kell arra, hogy a metódusban futtatott kód minél gyorsabban legyen, mert amíg a metódus nem fut le, a többi bejövő képkocka el lesz dobva. Ha az eldobások huzamosabb ideig fennmaradnak akkor az analyze
metódus hívási sűrűsége a teljesítményhez igazodik.
Jelenleg a leggyorsabb megoldásnak a következő bizonyult:
A bejövő 1920x1080 INPUT_RES
felbontású képből egy ugyanilyen arányú (16:9), de sokkal kisebb képet kérünk. A jelenlegi esetben ez 64x48 PROC_RES
felbontású. Ezzel azt érjük el, hogy az analyze
metódusban már csak egy 64x48x3 mátrixot kapunk és nem kell processzorral átlag színeket számolni sok képkockából hanem helyettünk ezt a beépített grafikus chip sokkal gyorsabban elvégzi.
Ez egy opcionálisan ki be kapcsolható lehetőség. A jelenség azzal kapcsolatos, hogy hogyan dolgozza fel a szemünk a led szalag által kibocsátott fényt. Részletesen nem szeretnék belemenni, ezen a linken lehet többet olvasni a jelenségről. Technikai megvalósítása abból ál, hogy van egy 255 elemű tömb amiben előre meghatározott értékek vannak (gamma tábla). Így ha a számított értéket indexként adunk meg a tömbnek egy előre maghatározott (korrigált) értéket kapunk vissza.
A három szín összetevőhöz (RGB), állíthatunk egy erősségiértéket. Ezzel korrigálható ha valamelyik színt erősebben jelenít meg a led szalag mint azt kellene. Technikai megvalósítása nagyon egyszerű a szín az egyes színek számításkor 0-tól 1-ig terjedő értékekkel kerül beszorzásra.
A kijelző irányítása az RPLCD.gpio
-ból a CharLCD
osztállyal történik. Semmi speciális tennivaló nincs. Az osztályt inicializálni kell a kijelző tulajdonságaival és bekötési adatokkal. Ebben az esetben ez egy 16 oszlopos 2 soros kijelző és a hardware sémának megfelelő GPIO kimenetek.
lcd = CharLCD(pin_rs=22, pin_rw=None, pin_e=27, pins_data=[6,13,19,26], numbering_mode=GPIO.BCM, cols=16, rows=2)
Ezekután az objektum write_string
metódusával írunk a kijelzőre és a cursor_pos
metódussal megadhatjuk, hogy pontosan melyik helyre szeretnénk írni.
Gomboknál sincs semmi különös. A gpiozero
könyvtár Button
osztályával vannak használva. Osztály inicializálásakor megadjuk, hogy melyik GPIO csatlakozóra van kötve.
up = Button(17)
Majd a when_deactivated
vagy when_activated
változóknak megadunk egy callback funkciót amit a könyvtár meghív ha a gombot elengedik vagy lenyomják. Ezeken kívül egy másik hasznos esemény a when_held
ami akkor aktiválódik ha a gombot N másodpercikg tartjuk. Alapértelmezetten ez 1 másodperc.
A fentebb leírtakat felhasználva készítettem egy LCDMenu
osztályt. Ami egy 2 soros 4 gombos menüt vezérel. Az első sorban a menü pont neve olvasható ezek között a fel és le gombokkal lehet váltani. A második sorban az adott menü pont értéke olvasható amit a jobbra és ballra gombokkal lehet változtatni. Érték változtatás esetén meghívódik a menü pont létrehozásánál megadott callback funkció az új értékkel. Ha az értékadó gombokat (jobbra, balra) hosszan tartjuk gyorsan tudunk váltani az értékek között, viszont ilyenkor csak a gomb elengedése pillanatában hívódik meg a callback funkció, ezzel elkerülve a felesleges műveleteket.
A menü inicializálása lcd és a négy gomb objektumával történik.
menu = LCDMenu(lcd, up, down, left, right)
Menü elemet pedig az LCDMenu
osztály addItem
metódusával tudunk hozzáadni. Megadva a menü azonosítóját(key
), megnevezését(name
) és opcionálisan választható értékek tuple-t (values
) és a callback funkciót (onChange
)
Ha a values
nélkül veszünk fel új menü pontot, akkor automatikusan egy szám lesz az érték amit a gombok segítségével eggyel tudunk növelni vagy csökkenteni.
menu.addItem("opt1", "Option 1", onChange=onOpt1Changed)
A másik amikor a values
-nak valamilyen listát, ranget vagy tuple-t adunk. Ilyenkor az abban található értékek fognak körkörösen változni a gombok aktiválására.
menu.addItem("clr", "Colors", ("Red", "Green", "Blue", "Black", "White", "Grey", "Yellow", "Pink"), onChange=onClrChanged)
A fentieken kívül van lehetőség egy adott beállított értékkel létrehozni egy menüt elemet. Ez az értéket az initValue
paraméterbe tudjuk átadni.
menu.addItem("brght", "Brightness", range(0,255), onChange=changeBrightness, initValue=100)
Az onChange
paraméterbe két féle értéket tudunk átadni. Sima funkció hivatkozást és egy tuple-t aminek az első értéke funkció hivatkozás a második értéke pedig egy értékeket tartalmazó tuple. Az első esetben a callback funkció csak a változás eredményével lesz meghívva.
def myCallbackFnc(val):
print(val)
...
menu.addItem("item1", "Item 1", range(0,256), onChange=myCallbackFnc, initValue=someVal)
A második eseten a változás eredményén kívül (Ami mindig az első paraméter) a tuple-ben átadott változókat is berakja paraméternek.
def myCallbackFnc(val, param1, param2):
print(f'value:{val} params:{param1},{param2}')
...
menu.addItem("item1", "Item 1", range(0,256), onChange=(myCallbackFnc, (callbackParam1, callbackParam2)), initValue=someVal)
A sima menü elemen kívül van lehetőség adat megjelenítő menü elem felvételére. A fentebb leírt menü elemtől ez abban különbözik, hogy nem érték kiválasztásra hanem érték megjelenítésre való. onChange
paraméter helyett sampleFrom
paramétere van. Ebbe egy tuple-t kell megadni, aminek első eleme egy függvényre mutat, második eleme meg egy szám ami funkció hívásának gyakoriságát jelöli másodpercben.
menu.addMonitorItem("fps", "Framerate", sampleFrom=(frameProc.getFramerate, 0.5))
Ez a menü elem fél másodpercenként fogja megjeleníteni a getFramerate
metódust visszatérési értékét.
A fenti menü rendszer segítségével ki/be kapcsolható egy beépített webszerver. A raspberry ilyenkor elindít egy wifi hotspot-ot amire rákapcsolódva elérhető egy weboldal. A weboldal összetevőit a www mappa tartalmazza. A webszerver az AmbiPiWeb
osztályon keresztül érhető el. Ennek az osztálynak a startServer
és stopServer
metódusaival be- és kikapcsolható a wifi hotspot és a webszerver együtt. Ezeken kívül van még egy addApi
metódusa amivel egy api végpontot köthetünk egy funkcióhíváshoz. Ennek kettő paramétere van, az api végpont címe és a visszatérési értéket adó függvény. A függvény a menü elemekhez hasonlóan lehet paraméter nélküli, ez esetben csak a függvényt kell átadni vagy paraméteres, ilyenkor egy tuple-t kell átadni aminek első eleme a függvény második eleme meg egy paramétereket tartalmazó tuple.
web.addApi("/api/fpstemp", db.getStatistics)
web.addApi("/api/fpstemplive", (liveStatistics, (frameProc,)))
Jelen feladatban a megjelenített weboldal egy chartJS diagrammot tartalmaz ami az FPS-t és a CPU hőmérsékletét mutatja. Ez betöltésnél az adatbázisból vett 100 adat alapján rajzolja meg a diagramot majd másodpercenként frissül élő adatokból.