Software - MaksymOnGit/ambiPi GitHub Wiki

Felhasznált külső könyvtárak

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.

Képelemzés

Könyvtár használat ismertetés

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.

Szükséges színek kigyűjtése

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.

Gamma korrekció

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.

Szín korrekció

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.

Interfész és menü

Kijelző

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.

Gombok

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.

Menü rendszer

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.

Webes felület

Web komponens

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,)))

Adatok

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.

⚠️ **GitHub.com Fallback** ⚠️