Nemrég sikerült dobogós helyezést elérnem egy versenyen, ahol a nyeremény egy Amazon utalvány volt. Viszont az amerikai amazonhoz szólt, és annyira nem is volt jelentős összegről szó, hogy Európába szállítással együtt sok lehetőséget adjon. Elkezdtem hát gondolkodni, hogy mire lenne szükségem.
Arra jutottam, hogy mostanában nagyon keveset van könyv a kezemben. Pár évvel ezelőtt imádtam olvasni és simán kiolvastam egy nap alatt 700-800 oldalas könyveket. Mostanában viszont hiába vettem könyvet a kezembe, az első 100 oldal után mind feledésbe merült. Nem sok hely van a táskámban, aztán előbb-utóbb kipakoltam a könyvet és soha nem tértem vissza rá.
A másik, hogy szerettem volna németül és angolul is elolvasni a kedvenc könyveim, azt viszont nem akartam, hogy a polcon heverjen ugyanaz a könyv magyar, angol és német kiadásban...
Régebben volt egy PocketBookom és imádtam. Kemény öt évet lehúzott, aztán feladta a harcot a tárhely, majd azt megjavítva felpúposodott az akksi. Ezen a ponton végleg kidobtam.
A régi eBook akksija alakult, mint púpos gyerek a prés alatt
Így tehát könyvek nélkül maradtam, az élet meg ment tovább. Egyre gyorsabban. Bár az igazat megvallva, egyre gyakrabban kacsintgattam vissza, hogy márpedig a sok film, zene és sorozat mellé bizony újraolvashatnám legalább a kedvenceim. Ezért jött kapóra tehát a nyeremény: pont elég egy belépő szintű olvasóra.
Kis kutatómunkát követően egy PocketBook Basic Lux 4re esett a választásom, ez volt ugyanis az a modell, ami pici ráfizetéssel ugyan, de a legjobb specifikációkat hozta ki a keretemből és elérhető volt az amerikai Amazonról.
Az eszköz teljes valójában
Kb egy hónapot vártam rá, mire kihozták. Megjárta a fél világot, mégis ideért sértetlenül. Be is üzemeltem és három könyvön már át is rágtam magam. Összességében elégedett vagyok a cuccal, noha tisztában voltam vele, hogy lesznek kompromisszumai. Nagyon próbálok vigyázni rá, mert az anyaga bizony gyenge. Koszos kézzel tapintva rangadnak rá az olajfoltok. És kicsit lomha néha. Illetve ami eléggé irritál, hogy valamiért olvasás közben néha nem enged továbblapozni, ilyenkor teljesen lefagy. Csak a reboot segít rajta, ekkor viszont elfelejti hol tartottam és a korábbi feloldás állapotába rakja a könyvet. Ez elég irritáló tud lenni, ha az ember halad 50-60 oldalt, aztán keresheti, hol tartott. Tervezem kipróbálni majd a Koreadert, hátha csak a gyári könyvolvasó bugos.
De amúgy a kijelzője nagyon jó. Mindig lenyűgöznek ezek az e-inkek. Illetve ebben már van háttérvilágítás is, szóval sötétben is tök baráti olvasni vele.
Ami még technikai változás az életemben, hogy kipróbáltam a butatelefonos életet. Így is herótom van attól, hogy lassan kényszeressé vált, hogy ha épp nincs mit nézni a telefonomon út közben, akkor Telexet pörgetek. Szereztem egy ilyen Nokiát:
Nokia 110 4G
Egy pár napig kifejezetten élmény volt, pl. rájöttem, hogy mennyire élvezem a rádiózást. Kitapasztaltam, hogy a RockFM a kedvenc, mert ott a legkevesebb a reklám pl. Rokonok ugyanúgy elértek, nekem pedig sokkal több időm lett hirtelen. A telefont se kellett naponta tölteni.
Viszont két dolog nagyon hiányzott: az egyik a MÁV app, ugyanis néhány jegyet csak a mobilapp enged vásárolni (meg nyilván a kasszás), amit utána nem lehet kinyomtatva bemutatni, mert az úgy érvénytelen. A másik a navigáció. Bár ez utóbbit le tudtam cserélni azzal, hogy idegesítettem random járókelőket, hogy segítsenek nekem...
Egy pár napja belegondoltam, hogy az eBook mindig nálam van a vonaton és tökéletes lenne jegyet prezentálni, hiszen a jegykép is statikus, WiFi pedig van az eszközön... Szóval elhatároztam, hogy írok egy MÁV kompatibilis appot az eszközre. Ok, de mégis hogy?
A kutatómunka kezdete
Gyors utánanéztem és kiderült, hogy van hivatalos támogatás harmadik feles alkalmazások fejlesztésére a platformon. Kicsit kevés az infó gyártói részről a hogyanről, van viszont publikálva egy fejlesztői SDK, illetve van egy ezer éve nem frissített dokumentáció is.
Több, mások által készített alkalmazást átnézve arra jutottam, hogy mindenki ezt a rejtélyes libinkview.so
-t használja és vagy C-ben, vagy C++ban fejleszt az olvasóra. Ez az inkview könyvtár felelős azért, hogy megjelenítsen tartalmakat a kijelzőn, kezelje az érintéseket, inicializálja az appot stb. Rengeteg mindent csinál. Viszont zárt forrású és a gyártó nem éppen híres arról, hogy gyakran frissítené a korábban linkelt SDK-t.
Elgondolkoztam, hogy nem lenne rossz C-ben írni az appom, viszont sose írtam benne GUI appot és nem biztos, hogy most akarnám megtanulni, pláne, hogy a jövőben szeretnék modern és kényelmesen többszálasítható appokat gyártani. A másik hátránya az inkview könyvtárnak, hogy a gyártó nem egyszer frissítette a könyvtárat és tört el vele funkcionalitást... Találtam egyébként Rust és Go bindingokat, amik nem csinálnak mást, csak a C++ PocketBook inkview implementációt hívják meg, ezáltal elérhetővé téve a funkcionalítást az adott programnyelven. Azonban a rust verzió nem is működött, a Go verzióból meg csomó dolog hiányzott. De a legrosszabb, hogy csak pixelenként lehet képet kirakni a kijelzőre, ami azt jelenti, hogy csomó felesleges C++ függvényhívást kell majd a kódból eszközölni, ami lassú és memóriaigényes. Egyetemen belénk lett verve, hogy a rekurzív megoldások mindig rosszabbak, mint az iteratívak.
Ezen a ponton átfutott az agyamon az ördögi terv, hogy mi lenne, ha direktben valami memory safe nyelvből frissíteném a kijelzőt. Sajnos azonban ezeken a ketyeréken nincs root hozzáférés, minden app egy reader felhasználó alatt fut. Direktben tehát nem biztos, hogy hozzáférünk a display driverhez. Viszont ha úgy közelítjük meg a dolgot, hogy az inkview tud rajzolni a képernyőre, ami ugyanúgy a programunkba töltődik be (tehát az is readerként fut), akkor már nem tűnik lehetetlennek a feladat.
Adott tehát a feladat: fejtsük meg, hogyan tudunk rajzolni a kijelzőre!
Rootolás
A tesztelések idejére legalább nem árt, ha van root és valami konzolos hozzáférés az eBook olvasóhoz. Bár hivatalosan nem rootolhatóak az eszközök, "szerencsére" elég régi forrásokból dolgozik a gyártó, amire vannak root exploitok. Valaki csinált is egy fantasztikus rootoló toolt, itt megtalálható. Amellett, hogy rootol, képes SSH-t nyitni, illetve a dolog zsenialitása még, hogy rájöttek: az USB működik OTG módban, tehát a megfelelő kernelmodult betöltve USB internetmegosztásra alkalmas eszközként is tudja magát hirdetni az olvasó. Ezen pedig lehet SSH-t csinálni. A fantasztikus az egészben, hogy pár kattintás volt az egész és azonnal elértem a Linuxos gépemen az ebook olvasót a megadott IP címen.
Most, hogy szereztünk konzolt, nézzünk egy CPU infót:
/mnt/secure # cat /proc/cpuinfo
processor : 0
model name : ARMv7 Processor rev 5 (v7l)
BogoMIPS : 11.42
Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xc07
CPU revision : 5
processor : 1
model name : ARMv7 Processor rev 5 (v7l)
BogoMIPS : 11.42
Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xc07
CPU revision : 5
Hardware : sun8iw10
Revision : 0000
Serial : 0000000000000000
Ez alapján nem valami erőgép, egy 1GHz-en ketyegő, 2 magos Allwinner B288 SoC mellé társul 512 MiB RAM memória és 8 GiB tárhely.
Kicsit szenvedtem vele, hogy egy hello world leforduljon armv7-re a PocketBook toolchain segítségével, aztán inkább jegeltem egyelőre a projektet, mert Go alatt túl egyszerű volt működésre bírni azt, amin rust alatt megszenvedtem.
Node, keressük meg az inkview-et:
/mnt/secure # find / -name libinkview.so
/ebrmain/cramfs/lib/libinkview.so
/ebrmain/lib/libinkview.so
Remek. Mivel mind a két lib ugyanaz, mindegy melyiket szedjük le:
/ebrmain/lib # sha256sum /ebrmain/cramfs/lib/libinkview.so
9659281ab2f8045401189f6db988117cdc90ab37b1ea6a5bd0a393d05dd014ac /ebrmain/cramfs/lib/libinkview.so
/ebrmain/lib # sha256sum /ebrmain/lib/libinkview.so
9659281ab2f8045401189f6db988117cdc90ab37b1ea6a5bd0a393d05dd014ac /ebrmain/lib/libinkview.so
Szedjük is le (ezt a gépemről kiadva):
scp root@169.254.0.1:/ebrmain/lib/libinkview.so ~/Documents/inkview/
Illetve nézzük meg, hogy milyen függőségei vannak:
[steve@todo inkview]$ readelf -a libinkview.so | grep NEEDED
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libssl.so.1.0.0]
0x00000001 (NEEDED) Shared library: [libcrypto.so.1.0.0]
0x00000001 (NEEDED) Shared library: [libfreetype.so.6]
0x00000001 (NEEDED) Shared library: [libjpeg.so.8]
0x00000001 (NEEDED) Shared library: [libtiff.so.5]
0x00000001 (NEEDED) Shared library: [libpng12.so.0]
0x00000001 (NEEDED) Shared library: [libz.so.1]
0x00000001 (NEEDED) Shared library: [libcurl.so.4]
0x00000001 (NEEDED) Shared library: [libicuuc.so.58]
0x00000001 (NEEDED) Shared library: [libicui18n.so.58]
0x00000001 (NEEDED) Shared library: [libicuio.so.58]
0x00000001 (NEEDED) Shared library: [libdl.so.2]
0x00000001 (NEEDED) Shared library: [librt.so.1]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libhwconfig.so]
0x00000001 (NEEDED) Shared library: [libstdc++.so.6]
0x00000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x00000001 (NEEDED) Shared library: [ld-linux.so.3]
Innen a libhwconfig.so
érdekes, így azt is lehúztam a /ebrmain/lib/libhwconfig.so
helyről. Szerencsére ennek már jóval kevesebb függősége van, mint a társának:
[steve@todo inkview]$ readelf -a libhwconfig.so | grep NEEDED
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libdl.so.2]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libc.so.6]
Úgyhogy kezdődhet is a visszafejtés.
Nullák és egyek
Sajnos az IDA Free nem támogatja a 32 bites ARM binárisokat, így Ghidra segítségével álltam neki a mókának. A visszafejtett kód kicsit nehezen volt követhető, ugyanis csomó függvény beállít egy-egy változót a memóriában, amit aztán a többi függvény próbál majd elérni. Kis extra nehézséget jelent, hogy minden érték kiolvasásnál meg kell keresni, hogy az adott változó hol kapott előtte értéket. Pl a képernyő frissítések tipikusan így működnek, hogy egy érdekes memóriaterületre ír az inkview, amit látszólag sehol nem inicializál.
Aztán kicsit kutatva rájöttem, hogy a képernyő tényleges inicializálása nem is a libinkview feladata, hanem a korábban talált libhwconfig.so
fogja ezt a nemes feladatot elvégezni. Magának a panelnek a kezelése pedig egyszerű, mivel standard Linux framebuffert használ. Csomó félkész és érdekes projektet találni GIthubon a témával kapcsolatban.
Azzal most senkit nem ijesztenék el, hogy pontosan hogy néz ki az inicializáló kód, de kicsit alakítva/emésztve egész egyszerű és standard.
Első lépésként megnyitja a /dev/fb0
eszközt a könyvtár, majd ha ez sikertelen, akkor próbálkozik a /dev/graphics/fb0
-el is. Nálam szerencsére már /dev/fb0
is van. Minden bizonnyal ez lesz az eszköz, amit a kernelmodul hoz létre nekünk és ezen keresztül tudunk majd a driverrel beszélgetni. Aztán jön egy érdekes rész, ugyanis a megnyitott fájlon beállít egy FD_CLOEXEC
flaget. Ez annyit jelent, hogy bármely exec*()
hívást követően a kernel be fogja zárni a fájlt. Annyira nekünk nem lényeges, de leimplementálhatjuk mi is. Utána kiolvassa a megjelenítő felbontását, illetve a memória kezdetét és méretét. Hogyan? Nos, kernelmodulok és userspace alkalmazások közti kommunikációra gyakori, hogy ioctleket használnak. Mivel van kernel forrásunk szerencsére, kinézhetjük, hogy pontosan mi lesz ez a két cím. De egyébként ez a binárisba is bele van égetve. Nézzük:
/* ioctls
0x46 is 'F' */
#define FBIOGET_VSCREENINFO 0x4600
#define FBIOPUT_VSCREENINFO 0x4601
#define FBIOGET_FSCREENINFO 0x4602
Szuper. Mivel a bináris a 0x4600
-ot és a 0x4602
-t hívja meg, tudjuk, hogy az FBIOGET_VSCREENINFO
és FBIOGET_FSCREENINFO
fog majd kelleni.
Ezek az ioctl-ek érdekes dolgok, ugyanis tudunk velük struktúrákat küldeni és fogadni is. Pontosan ezt csinálja a bináris is. Lekéri a két ioctl-t és az eredményt letárolja egy fixinfo és egy varinfo struktúrában.
Most vagyunk azon a ponton, hogy meg tudjuk nyitni a framebuffert, illetve ki tudunk olvasni adatokat róla. Akit érdekel az eddigi kód, az itt megtalálja.
Ezt gyorsan le is fordítottam az eszközre:
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-s -w" .
Majd következett a nagy kérdés: hova rakjam átmenetileg. Lehetőleg nem az SD kártyát kéne gyilkolni a tesztelések alatt, valami ramdiszk jobb lenne. Sajnos a /tmp itt nem ramdiszk:
/var/dev/shm # df -h /tmp/
Filesystem Size Used Available Use% Mounted on
none 128.0M 900.0K 127.1M 1% /var
Viszont a /dev/shm szerencsére az:
/var/dev/shm # df -h /dev/shm/
Filesystem Size Used Available Use% Mounted on
tmpfs 250.4M 7.1M 243.4M 3% /var/dev/shm
Úgyhogy ide egész nyugodtan garázdálkodhatunk! SCP fel is pakolja az ebookra a binárist szépen:
scp ./einkexp root@169.254.0.1:/dev/shm/
És futtatásra kész az alig másfél megás binárisunk. Nézzük, mit produkál:
/var/dev/shm # ./einkexp
2025/02/24 16:01:04 Framebuffer: 1024x758, 8 bpp
2025/02/24 16:01:04 Framebuffer memory: 778240 bytes
2025/02/24 16:01:04 Line length: 1024 bytes
2025/02/24 16:01:04 Framebuffer finfo: {Id:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] SmemStart:1564479488 Smem
Len:778240 Type:0 TypeAux:0 Visual:2 XpanStep:1 YpanStep:1 YwrapStep:0 LineLength:1024 MmioStart:0 Mmi
oLen:0 Accel:0 Capabilities:0 Reserved:[0 0]}
2025/02/24 16:01:04 Framebuffer vinfo: {XRes:1024 YRes:758 XResVirtual:1024 YResVirtual:758 XOffset:0
YOffset:0 BitsPerPixel:8 Grayscale:0 Red:{Offset:5 Length:3 MsbRight:0} Green:{Offset:2 Length:3 MsbRi
ght:0} Blue:{Offset:0 Length:2 MsbRight:0} Transp:{Offset:0 Length:0 MsbRight:0} NonStd:0 Activate:128
Height:0 Width:0 AccelFlags:0 PixClock:0 LeftMargin:0 RightMargin:0 UpperMargin:0 LowerMargin:0 HSync
Len:0 VSyncLen:0 Sync:0 VMode:0 Rotate:2 Colorspace:0 Reserved:[0 0 0 0]}
Fantasztikus! Úgy néz ki, mindent sikerült kiolvasni elsőre. Ilyen is ritkán van...
Képletes képleképezés
Ez eddig tök jó, de mi a gyakorlati haszna az egésznek az információszerzésen kívül?! Nekünk kép kéne... Nos, van még egy dolog, amit az inicializálás során csinál a gyári bináris. Méghozzá fogja, és mmap-olja a teljes /dev/fb0
-t, amit korábban megnyitottunk. Ha nem tudod mi az az mmap
, nem baj. Nagyon érdekes hívás, ugyanis beadsz neki egy fájlt, meg egy megfelelően lefoglalt memóriaterületet, illetve egy hosszat, az meg az általunk megadott memóriaterületre "kiterítve" címezhetővé teszi a fájlt. Ez a gyakorlatban így néz ki:
fbData, err := syscall.Mmap(int(fd), 0, int(finfo.SmemLen), syscall.PROT_WRITE, syscall.MAP_SHARED)
if err != nil {
log.Printf("Failed to mmap framebuffer: %v\n", err)
os.Exit(1)
}
defer syscall.Munmap(fbData)
Szóval most ott tartunk, hogy megnyitottuk a framebuffert, tudjuk csomó paraméterét, hozzáférünk memóriában és tök egyszerűen tudnánk írni. Igen ám, de hogy? Ez már nincs benne az inicializáló kódban. Itt jön képbe az inkviewben az a sok, látszólag nem inicializált memóriaterület. Úgy néz ki, hogy a /dev/fb0 fájlba írva közvetlenül tudjuk a pixelek adatait manipulálni. Ez is egy elég standard megoldása a dolognak.
Mivel szürkeárnyalatos kijelzőm van, a hivatalos inkview doksi szerint is 8 bittel dolgozva, az alábbi megoldással tudok egy szürkeárnyalatos képet kiírni a kijelzőre:
func copyImageToFramebuffer(fbData []byte, img *image.Gray, width, height, lineLength int) {
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
offset := y*lineLength + x
fbData[offset] = img.GrayAt(x, y).Y
}
}
}
Ugye a szélesség, a magasság és a "sormagasság" mind a korábbi infókból jön. Már csak a kép szürkeárnyalatossá alakítása van hátra. Ehhez az alábbi kóddal álltam elő:
func convertToGrayscale(img image.Image, width, height int) *image.Gray {
gray := image.NewGray(image.Rect(0, 0, width, height))
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
r, g, b, _ := img.At(x, y).RGBA()
grayVal := uint8((r*299 + g*587 + b*114) / 1000 >> 8)
gray.SetGray(x, y, color.Gray{Y: grayVal})
}
}
return gray
}
Igen ám, de az egészet összerakva, majd lefuttatva semmi hibaüzenet nem jön... De frissítés sem. Konkrétan semmi nem történik a kijelzőn. Kicsit elszomorodtam, de aztán beugrott, hogy azért mégiscsak e-ink panelekről beszélünk, ahol a képfrissítés "drága" művelet. Eszi az akkut és a panel életét is jelentősen csökkenti. Ezért nincs értelme folyamatosan frissíteni, ahogy íródik a buffer. És ezért rendelkezik az inkview pl. egy FullUpdate, FullUpdateHQ és egy PartialUpdate metóduscsomaggal is. Már csak arra kellett rájönni, hogy ez mégis hogy működik.
Képfrissítés
Ahogy nézegettem a binárist, hamar belevesztem a részletekbe, mert itt aztán párhuzamos programozástól elkezdve a virtuális függvényhíváson át a dinamikusan allokált memóriáig minden van. Sikerült megtalálni a függvényt, ami indítja a frissítést, de azt nem, ami ténylegesen frissíti a kijelzőt...
Elkezdtem máshogy közelíteni a dolgot és utánanéztem, hogy létezik-e esetleg direktben panel framebufferre író alkalmazás PocketBookra. Hamar megtaláltam az FBInket, ami egy egyszerű GUI keretrendszer C-ben rengeteg különbözö ebook márka eszközére. Ebben volt egy PocketBook támogatásról szóló pull request.
Szerencsére ők valahogy megtalálták egy hasonló PocketBook kernelmodul forrását, ami alapján meglett, hogy ez is egy ioctl, amivel most nem adatokat töltünk egy structba, mint korábban, hanem adatokat küldünk. Az ioctl itt van definiálva: [link]. Csak egy a gond, a címét nem ismerjük, mivel makróval van sajnos megadva. De erre is találtam megoldást, itt:
static const int MXCFB_SEND_UPDATE = 1077954094;
Szóval az egészet a linkelt C kód alapján összerakva az alábbi megoldást pakoltam össze:
func sendFramebufferUpdate(fd int, width, height int) error {
region := mxcfbRect{
Top: 0,
Left: 0,
Width: uint32(width),
Height: uint32(height),
}
update := mxcfbUpdateData{
UpdateRegion: region,
WaveformMode: WAVEFORM_MODE_DU,
UpdateMode: 0x1, // Full update
UpdateMarker: 0,
Temp: 24,
Flags: EPDC_FLAG_USE_DITHERING_Y1,
AltBufferData: mxcfbAltBufferData{
PhysAddr: 0,
Width: uint32(width),
Height: uint32(height),
},
}
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(MXCFB_SEND_UPDATE), uintptr(unsafe.Pointer(&update)))
if errno != 0 {
return fmt.Errorf("ioctl MXCFB_SEND_UPDATE failed: %v", errno)
}
return nil
}
A struktúrákat nem másoltam át, mert az gyakorlatilag 1:1 portja a C kódnak és a végén úgyis beszúrom majd a végeredményt. Viszont ami érdekes, az aWAVEFORM_MODE_DU
. Ahogy így olvasgattam a témában, kiderült számomra, hogy ezeknél az ebookoknál kb mindenhol ugyanaz a Carta panel van használva. Ami viszont a legtöbb gyártónál egyedi és féltve örzőtt titok, az ez a waveform algoritmus. Nehéz betalálni, hogy mi az, amit még bír a panel, ugyanakkor a képfrissítés is egészségesnek hat a felhasználó számára, gyorsan végbemegy és szép kontrasztot is nyújt. AWAVEFORM_MODE_DU
úgy néz ki a PocketBook egyik alapértelmezése. Egyelőre hagytam ezen, mert ez volt az FBInkbe is beégetve. A Temp érték a panel hőmérsékletét hivatott állítani celsiusban, a cél azt az értéket megadni, ami a frissítéskor a panelen tapasztalható. A dithering paraméterre még nem jöttem rá.
Viszont összeraktam a teljes kódot. Az eredményt pedig felraktam ide. A korábbiakhoz hasonlóan ezt is lefordítottam, feltöltöttem egy test.png-t szintén a /dev/shm alá, aztán lefuttattam és vártam a csodát.
Igen, tényleg ez volt az első fekete-fehér kép, ami eszembe jutott
De az eredmény legalább magért beszél. Szinte azonnal frissítette a képet és teljesen letörölte előtte a kijelzőt, tehát a full update is remekül működik. SIkerült tehát rajzolni a kijelzőre.
Nyilván még hosszú az út ahhoz, hogy működő programokat tudjunk fejleszteni, hiszen pl a touch panellel még egyáltalán nem is foglalkoztunk, de azért ez se semmi. Direktben tudjuk a kijelzőt frissíteni anélkül, hogy a gyártói könyvtárra hagyatkoznánk direktben Go-ból.
A következő cikkben tákolunk valami kezdetleges GUI appot is, hiszen az alapok már kész vannak a megjelenítéshez.
Köszi, hogy elolvastad!