Tema 2 - RPI/GPIO Internet of Trees (2025)

  • Publicare:
    • 15 Decembrie 2025 18:00
  • Termen de predare:
    • 18 Ianuarie 2025 23:55 - deadline HARD

Obiective:

  • construirea unei distribuții Linux minimaliste;
  • configurări de bază Linux;
  • servicii de sistem Linux;
  • dezvoltare aplicații IoT;
  • optimizări de spațiu;
  • utilizare GPIOs din aplicațiile Linux;

Pentru a rezolva tema, este recomandat să folosiți mașina virtuală SI 2025 cu toate uneltele necesare gata instalate!

Enunț

Dorim să realizăm un firmware minimalist pentru un dispozitiv IoT ce va controla niște LED-uri (“ipotetice”) prin GPIO + aplicație web folosind un Raspberry PI (emulate folosind qemu).

Pe scurt, va trebui să realizați o imagine incorporabilă cu Linux ce va implementa o aplicație ce va controla o presupusă instalație de lumini a bradului de Crăciun prin GPIO (folosind Linux GPIO API) și expune pe rețea un server HTTP cu o pagină web de vizualizare și control a luminilor (aici aveți libertate deplină asupra desenului / animațiilor / controalelor oferite).

Exemplu de frontend web (aspectul nu contează, dar puteți să faceți ceva mai drăguț dacă aveți timp/chef pentru bonus):

Cerințe

Imaginea Linux de bază

Pas inițial: realizarea unei imagini de bază de Linux (kernel + rootfs) ce va fi folosită ca punct de plecare / suport pentru programele & serviciile cerute de restul cerințelor:

  • Imaginea trebuie construită din componente de la zero: kernel compilat + rootfs;
  • Rootfs-ul să ocupe cât mai puțin posibil (vedeți mai jos punctajele); se poate folosi orice distribuție / abordare de generat rootfs (atât printre cele studiate la laborator, cât și celelalte populare în industrie):
    • Buildroot – recomandat, puteți obține cele mai reduse dimensiuni!
    • Debian, prin qemu + debootstrap – nerecomandat (se vor obține dimensiuni destul de mari, 500MB ~ 1GB) și vor fi depunctate datorită dimensiunii enorme, wear&tear-ul SSD-ului celui care corectează tema etc. (-20p);
    • Alpine Linux bootstrap-uit folosind apk.static – procedură similară cu debootstrap, însă se vor obține imagini destul de mici (recomandat pentru avansați, puteți folosi scriptul alpine-chroot-install, dar studiați-i codul bine înainte)!
    • Yocto Linux - very steep learning curve, necesar 50GB + 8GB RAM pentru compilare, doar pentru cei care au mai folosit!.
    • NU se acceptă: rootfs gata construit / descărcabil (e.g., imaginea cu Debian / Raspberry OS);
  • Kernel compilat de voi cu LOCALVERSION="-si-<prenume.nume>" (altfel nu se punctează acest aspect! vedeți mai jos);
  • Sistemul (rootfs + kernel) să fie compilat pe AArch64 (i.e.: arm64) și să poată fi rulat în qemu folosind machine type raspi3b (recomandat, raspi4b nu bootează ușor cu un kernel custom); vedeți mai jos config-uri de kernel testate deja pentru compatibilitate + aveți script de rulare în schelet;
  • Imaginea trebuie împachetată într-un fișier disk SD (a cărui dimensiune trebuie să fie o putere a lui 2, cum găsiți precizat prin tutoriale) – alegeți, însă, o dimensiune cât mai mică posibil (e.g., dacă imaginea voastră ocupă 70MB, creați disk SD de 128MB).

Sistemul trebuie să conțină următoarele configurații de bază (ne ajută pe noi, în special, să automatizăm partea de testare):

  • utilizator root cu parola tema2025 (obligatoriu: să ne putem autentifica în consola emulată);
  • hostname-ul tema2025;
  • să-și preia automat IP-ul folosind DHCP pe prima interfață disponibilă (recomandat: folosiți la rulare parametrul de kernel net.ifnames=0, astfel încât numele primei interfețe să fie eth0 pentru o configurație portabilă – setare deja prezentă în scriptul de rulare din schelet);
  • să ruleze SSH pe portul 22 (și să fie activată autentificarea cu root + parolă, aka setarea PermitRootLogin!);

Rezolvarea acestei cerințe este OBLIGATORIE pentru obținerea oricărui fel de punctaj (altfel nu avem ce testa ⇒ 0p)!

Inb4: veți avea de customizat rootfs-ul. După cum va trebui să separați fișierele / codul / scripturile sursă de imaginea binară obținută ca rezultat al procesului de build (care poate fi semi-manual sau complet automatizat – ce preferați), recomandăm organizarea unui overlay – subdirector al temei unde includeți DOAR fișierele de configurație / surse ce doriți să apară în imaginea finală ce le vor suprascrie pe cele implicite.

Pentru rootfs-ul construite prin tehnică de distro package-based bootstrapping, puteți copia ulterior acest overlay folosind cp -ar sau rsync -a.

Pentru BuildRoot, citiți recomandările oficiale de customizare (folosiți OVERLAY, e cel mai ușor + elegant).

+ citiți enunțul până la capăt pentru a vedea cerințele finale!

Compilare Kernel

Recomandăm folosirea kernelului mainline, descărcabil de pe https://kernel.org/ (sau Github, torvalds/linux). NU LUAȚI MASTER-ul sau alte branch-uri experimentale! Folosiți un branch/tag de versiune (e.g., v6.12).

La configurarea kernelului, va trebui să setați parametrul LOCALVERSION la valoarea -si-<prenume.nume> (folosiți username-ul de pe Moodle în loc de placeholderl <prenume.nume>, e.g. -si-florin.stoian!). Vedeți aici variabilele implicate, TLDR: nu uitați să dezactivați LOCALVERSION_AUTO pentru a putea set versiunea voastră custom!

Pentru o testare rapidă a sistemului rădăcină, puteți folosi imaginea vmlinuz-test inclusă în scheletul de temă în scheletul de temă (atenție: compatibil doar cu raspi3b cu device tree-ul bcm2837-rpi-3-b.dtb).

Au fost testate versiunile v6.12 și v6.6, compilate atât automat prin buildroot (însă se configurează mai special, vedeți mai jos), cât și manual. Așadar, aveți două opțiuni de compilare:

1. Compilare manuală

Pur și simplu se descarcă codul sursă torvalds/linux de pe git (ori din GitHub release, arhivă .tar.gz mult mai mică) și să dă make ca în laborator (nu uitați să modificați LOCALVERSION!)!

NU uitați ARCH=arm64 și configurația inițială a arhitecturii, defconfig (pe mainline NU există bcm27*_defconfig)!

Pentru a a nu avea probleme cu copierea/instalarea modulelor încărcabile dinamic (e.g.: mmc, să nu vadă rootfs-ul, sau să nu se încarce automat driverul de rețea usb-net), recomandăm dezactivarea modulelor de kernel, adică MODULES=n din menuconfig – acest lucru va face integrarea tuturor driverelor în imaginea de kernel și va omite copierea lor pe sistemul rădăcină (în <rootfs>/lib/modules). Alternativ, va trebui să instalați modulele .ko rezultate într-un overlay folosind make … modules_install și apoi copia pe rootfs-ul vostru!

2. Compilare automată prin Buildroot

Pentru cei care folosesc buildroot, aveți opțiunea de a compila kernelul Linux (în menuconfig, aveți BR2_LINUX_KERNEL cu toate opțiunile de descărcare necesare).

Însă procesul de adaptare a configurației de kernel necesită pași + documentare suplimentară (still pretty much recommended!):

  • va trebui să folosiți kernelul mainline (torvalds/linux); se recomandă utilizarea unei reguli de descărcare a arhivei tar.gz de pe GitHub (documentație aici cu ce să puneți la URL de descărcare) + exemplu la config-ul oficial Buildroot pentru raspberrypi;
  • va trebui ori să patchuiți linux/linux.hash și să adăugați hash-urile arhivei descărcate de pe GitHub asociate versiunii voastre, sau dezactiva de tot verificarea de hash prin opțiunea BR2_DOWNLOAD_FORCE_CHECK_HASHES=n (însă este mai complicat și necesită modificarea variabilei BR_NO_CHECK_HASH_FOR direct din Makefile, aceasta nu e expusă în Kconfig!!);
  • pentru a încărca automat modulele de kernel pe un rootfs de Buildroot, aveți nevoie de o soluție de gestiune a dispozitivelor Linux; puteți ori să folosiți systemd, ori va trebui configurat EUDEV (recomandat), exemplu de config ce trebuie efectuat (depinde de ce defconfig ați ales dacă sunt prezente sau nu):
    # se poate alege și biblioteca ucLibC, însă am avut probleme cu python3-flask
    BR2_TOOLCHAIN_BUILDROOT_MUSL=y
    BR2_SHARED_STATIC_LIBS=y
    BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
    • soluție alternativă ar fi să faceți ca la compilare manuală, să compilați tot kernel image-ul ca monolit, fără module încărcabile (MODULES=n)!
  • pentru a customiza kernelul (va trebui să modificați, cel puțin: LOCALVERSION*), aveți target-ul buildroot make linux-menuconfig (atenție: nu uitați să faceți save / backup la config-ul de kernel după modificare, citiți în manual cum);
Precizări comune la compilarea kernelului

Deși e prezent în defconfig (în caz că vreți să optimizați), nu uitați să verificați / includeți driverele de GPIO și USB Ethernet necesare testării!

La rularea prin qemu, trebuie să folosiți device tree-ul care începe cu bcm2837-, deoarece rulați kernel-ul mainline. Vedeți explicația aici sau aici. DTB-ul îl puteți compila voi (din kernel: make dtbs, sau, la Buildroot, aveți setare în meniu) sau prelua din altă parte (cât timp funcționează).

Dacă nu vedeți nimic la bootarea kernelului, înseamnă că ori imaginea sau device tree-ul sunt proaste (și este dificil de dat seama), ori consola serială implicită este configurată greșit din kernel command line. Asigurați-vă că aveți parametrii kernel de earlycon corecți pentru varianta de Raspberry PI emulată. Dacă vedeți mesaje inițiale în consolă, însă se blochează ulterior în procesul de boot și nu apare login prompt-ul, atunci înseamnă că parametrul console= al kernelului este incorect (încercați alt dispozitiv serial, e.g., ttyAMA<i>, ttyS<i> etc.).

Dacă aveți probleme cu kernelul și/sau nu rulează corect în qemu, atunci puteți omite / include un kernel pre-compilat, însă cu depunctare (-10p). Însă va trebui să scrieți în README ce ați încercat și ce rezultate ați avut!

HTTP Server / Web UI:

Dorim ca sistemul să expună un server HTTP pe portul 80 o interfață web minimalistă care să prezinte o diagramă a stării luminițelor (GPIO-urilor) și va oferi cel puțin o comandă de control (e.g., start / stop al animației, randomizat beculețele/GPIO-urile aprinse etc.).

  • Va trebui să rulați automat la boot, ca daemon, un server http ce va asculta pe portul 80;
  • NU contează așa mult ce desenați (e.g., puteți pune un fundal cu brad și poziționa luminițele prin coordonate custom pentru a ne pune în tema sărbătorilor mai bine, sau folosi o matrice simplă dacă nu e chef);
  • NU este obligatoriu să aveți animație real-time a stării LED-urilor / GPIO-urilor emulate! Este suficient să se schimbe la refresh sau automat cu perioadă mare (1-5 secunde); JavaScript-ul care să animeze realtime va fi considerat bonus!
  • Puteți folosi orice limbaj de programare / scripting / framework pentru a construi acest endpoint; exemple:
    • Python: http.server / flask / Django / etc.;
    • NodeJS: http / ExpressJS / other 1000s of libraries;
    • PHP (+ Apache / Nginx / Lighttpd etc.): cu sau fără framework;
    • Golang: net/http (bonus: dimensiuni mici ale aplicațiilor!);
    • Rust: http (built in libraries + dimensiuni ff. mici, la fel ca la GoLang!);
    • C/C++ (for hardcore developers): Facil / Oat++ / etc. – performanță++ && dimensiuni%–% ;)
  • Atenție: Dacă alegeți să compilați rootfs prin Buildroot, veți avea de scris/făcut rost de scripturi de compilare + împachetare atât pentru codul vostru + toate dependințele necesare (e.g., la Python PIP / VirtualEnv, pachete npm pentru NodeJS etc.)! Se poate, desigur, face cross compiling, însă trebuie folosit compilatorul de la Buildroot (same thing for Yocto)!
  • Dacă aplicația nu pornește automat (și veți fi depunctați), lăsați pe rootfs un script de pornire în /srv/!

Serverul trebuie să pornească automat la boot (ca daemon). Majoritatea distribuțiilor folosesc systemd ca init și manager de servicii, deci va trebui să creați un astfel de descriptor pentru aplicația web.

În BuildRoot și Yocto aveți mai multe opțiuni de init system-uri, la alegere: Busybox (cel mai light dintre toate, se scriu scripturi sh), SysVInit (aka rc.d/runlevels) sau SystemD (mai popular și bine documentat, însă trebuie compilat și poate adăuga ~1-2h în plus la timpul de compilare, depinde de puterea de calcul a sistemului).

Pentru Buildroot, dacă folosiți un interpretor / limbaj care necesită dependințe externe, citiți secțiunea Adding Packages. Pentru Python aveți incluse deja o mulțime de biblioteci populare. Pentru Golang, citiți secțiunea Infrastructure for Go packages a manualului. În general, pentru cei care doresc să folosească un limbaj compilat, este util ghidul general de generare a pachetelor compilate pentru build system-ul lui standard.

Interogarea și controlul GPIO-urilor puteți să o realizați fie folosind un alt serviciu / program separat, sau să fie thread nou în cadrul serverului web. Modul de interfațare va fi descris în subsecțiune următoare.

Dacă nu doriți să rezolvați și acel task, puteți să prezentați pur și simplu câteva date de test (i.e. hardcodate) în interfața Web.

GPIO Control

Pe lângă serviciul web, va trebui să dezvoltați un serviciu de control al GPIO-urilor, cu specificația:

  • Recomandarea ar fi să faceți un serviciu daemon care va primi comenzi de la serverul web și va rula o buclă internă de bit toggling pe interfața GPIO a Linux-ului.
  • Mai întâi, citiți aici documentație despre Linux GPIO API.
  • Va fi necesar să controlați doar primii 20 de GPIOs (pinii indexați 0-19 ai /dev/gpiochip0)
  • Atenție: numerotarea pinilor pe Raspberry PI NU corespunde cu organizarea logică a acestora!
    • În vizualizatorul de GPIO atașat de QEMU, s-a folosit o simplificare: pinii sunt indexați de la 0 la 20 și afișați pe 2 coloane!
  • Organizarea vizuală NU contează (e.g., dacă desenați în interfața web un brad, puteți atribui orice culoare / poziție oricărui pin); NU este necesară utilizarea tuturor pinilor, doar a unei majorități utile;
    • Vizualizatorul inclus NU necesită astfel de prezentare (e.g., forma unui brad), fiind folosit strict pentru a testa acționarea GPIO-urilor; opțional, ca bonus, puteți să-l modificați să ilustreze același lucru ca aplicația web;
  • Atenție: dacă doriți să folosiți culori RGB, veți avea nevoie de 3 GPIO-uri diferite, deci atenție ce planificați de la început!
  • De asemenea, pasul de animație nu ar trebui să fie mai rapid de 500ms (i.e., frecvență sub 2Hz);
  • NU faceți PWM + NU FOLOSIȚI întreruperi de GPIO (nu au fost implementate în qemu :( );
  • La fel ca la serviciul web, puteți folosi orice limbaj / biblioteci (chiar și bash) pentru acționarea GPIO-urilor, însă e recomandată biblioteca GPIOD dacă este disponibilă:
  • Puteți face comunicarea între cele 2 procese (de la serverul HTTP la daemonul de GPIO) prin orice mecanism vă pune Linux / limbajul la dispoziție (unix pipes, sockeți, RPC framework al limbajului, chiar și niște fișiere simple text + polling pentru o implementare rudimentară);
  • Deaemonul (dacă este separat) trebuie să pornească automat cu sistemul!
  • Dacă daemonul nu pornește automat (și veți fi depunctați), lăsați pe rootfs un script de pornire TOT în /srv/!

Vizualizator GPIO în consolă

Pentru a vedea dacă acționarea voastră de GPIO funcționează, punem la dispoziție în schelet un program CLI simplu de Python ce se conectează la QEmu prin protocolul qtest inspirat de aici: https://ihateyour.cloud/post/20201204-1.html.

Acesta face polling la starea GPIO-urilor în memoria VM-ului de qemu și afișează colorat pe ecran la intervale de 200ms (din acest motiv este impusă restricția de frecvență a animațiilor)!

Pentru testare, veți porni manual script-ul ce generează astfel de date în paralel cu qemu!

Implicit, socketul de qtest este creat de către scriptul de launch-tema2 folosind linia de comandă qemu. Dacă faceți ceva custom, nu uitați să includeți! De asemenea, vedeți calea unde este creat socketul de control qtest (prin /tmp și schimbați / ștergeți la nevoie)

Schelet temă

Ca și punct de pornire, puteți descărca un schelet inițial cu scripturi + structură recomandată (v0.1). Aceasta conține:

  • Makefile util pentru construirea arhivelor; obligatoriu să-l studiați + editați, nu face ce trebuie nemodificat!
  • script de testare launch-tema2.sh pe sistemul gazdă (va rula qemu cu fișierele kernel+imagine, vedeți mai jos, la conținutul arhivei, ce denumiri folosește);
  • un script Python gpio-viewer.py ce vizualizează starea regiștrilor hardware emulați ai GPIO-urilor, integrat cu launch-tema2.sh (comunică printr-un socket de qtest în /tmp, vedeți cod sursă);
  • o imagine de kernel vmlinuz-test compilată ca monolit (MODULES=n) cu strictul necesar de drivere (consolă serială PL011 & 8250, USBNET, IP_PNP, BRCM platform devices, GPIOs etc.);

Atenție: scriptul de launch-tema2.sh rulează qemu cu parametrul -qtest <socket_path>, ce va da eroare dacă nu există acel UNIX socket creat (de către aplicația gpio-viewer.py). Dacă doriți să rulați VM-ul FĂRĂ GPIO Viewer, folosiți make run A=--no-qtest sau direct ./launch-tema2.sh --no-qtest!

Trimitere

Deoarece Moodle nu acceptă dimensiuni foarte mari, soluția temei va fi împărțită și trimisă în două moduri (vă rugăm să respectați convențiile de denumire cu exactitate!):

  • arhivă cu codul sursă + Readme + hash și alte metainformații (vedeți mai jos) → pe Moodle
  • arhivă cu binarele / imaginea rulabilă → le urcați pe Sharepoint-ul contului Microsoft de student și dați share prin Link accesibil tuturor conturilor (sau, cel puțin cu permisiuni la florin.stancu@upb.ro), însă nepublicat (pe care ni-l trimiteți doar nouă în fișierul url.txt);

Arhiva cu binarele (.tar.*z pls; se acceptă gz și xz) trebuie să conțină (obligatoriu: să folosiți strict aceste denumiri de fișiere):

  • tema2.img: imaginea finală (format raw atașabil prin losetup, NU qcow sau altele!); poate conține sau nu partiții (dar va trebui să adaptați scriptul de rulare);
  • vmlinuz-tema2: imaginea kernel-ului compatibil cu QEMU;
  • launch-tema2.sh: script de pornire QEMU (vedeți scheletul dat);
  • includeți și scripturile suplimentare necesare pentru rulare, dacă mai sunt (e.g., viewerul de GPIO-uri în CLI, dacă l-ați modificat);
  • NU INCLUDEȚI: cache-ul de build al Buildroot / Yocto (poate avea 6-20 GB!), DOAR artefactele finale (copiați-le manual sau folosiți ceva similar ca în Makefile-ul din schelet)!
  • Această arhivă nu ar trebui să depășească 500MB (folosiți tar.xz pentru rată de compresie bună).

Pe Sharepoint, dați link CĂTRE FIȘIERUL ARHIVEI, NU CĂTRE UN ÎNTREG DIRECTOR!

Noi avem scripturi care automatizează descărcarea, veți fi depunctați dacă nu urmați aceste convenții! Ar trebui să meargă descărcate automat punând &download=1 la finalul URL-ului ;)

Arhiva cu fișierele sursă (.zip pls) OBLIGATORIU să conțină:

  • sursele aplicației (în orice limbaje ați ales), scripturi custom folosite pentru build și/sau overlay-ul copiat pe rootfs (orice ați inclus extra peste sistemul de bază – de preferat, organizat într-o structură Unix-like: ./etc/, ./usr/bin, ./opt/* etc.); NU COPIAȚI ROOTFS-UL GENERAT! (doar OVERLAY-ul, dacă ați folosit);
  • fișierele de configurație (.config) ale kernel și/sau buildroot – obligatoriu dacă e cazul! folosiți numele kernel_config și buildroot_config în arhiva cu sursele (în niciun caz nu le lăsați hidden!);
  • fișier README.txt cu explicații referitoare la modul de construire al imaginii, arhitectura soluției, configurații speciale de optimizare folosite etc.
  • fișier url.txt cu URL către arhiva .tar.*z a binarelor de pe Sharepoint! (uploadată anterior);
  • fișier checksum.txt care să conțină hash-ul SHA256 al arhivei cu binarele (obținut cu sha256sum); ATENȚIE: verificați și re-verificați (de încă 2 ori) conținutul fișierului la încărcare pe Moodle cu hash-ul real deoarece tema nu va fi punctată dacă diferă!
  • NU INCLUDEȚI: fișiere sursă ale Buildroot / Yocto / biblioteci / frameworkuri / VirtualEnv descărcabile de pe Internet (menționați în Readme ce ați folosit);
  • Hint: pentru a include un fișier la crearea automată a arhivei prin make source_archive, pur și simplu copiați-l pe rădăcină (sau într-un director care nu este ignorat – verificați Makefile-ul din schelet)!
  • Această arhivă nu ar trebui să depășească 1MB (aveți restricție pe Moodle).

Puteți folosi Makefile-ul din scheletul temei pentru generarea acestor două arhive, însă este recomandată verificarea manuală a arhivelor după generare!

Nu vor fi punctate temele care nu au hash-ul SHA256 al arhivei încărcat pe Moodle sau cele al căror hash nu corespunde cu arhiva downloadată de pe platforma de hosting la momentul corectării (este folosit și pentru a verifica upload / modificări apărute după deadline)!

Notare

Din 100p total, aveți:

  • (40p) Imaginea și funcționalitățile de bază (trebuie să ruleze în qemu!);
    • -20p depunctare pentru imaginea SD cu rootfs-ul ce depășește 256MB (testat cu interpretorul Python în Buildroot, tot se încadrează în limită!);
      • se contorizează fără kernel, acesta poate avea max. 64M – deși nu e imposibil să-l faceți s depășească această dimensiune, încercați să nu!
    • -10p depunctare pentru kernel necompilat de voi (ori îl folosiți pe cel furnizat de noi în schelet, ori cel furnizat de voi nu conține LOCALVERSION="-si-<prenume.nume>");
    • Notă: chiar și cu depunctare, a avea un sistem funcțional în qemu este obligatoriu pentru a lua restul de puncte acordate pe task-urile următoare!
  • (20p) Aplicația web de prezentare/control a luminițelor;
  • (30p) Daemonul/thread-ul care setează/animează GPIO-urile;
  • (10p) Readme scris clar și care descrie procesul urmat, arhitectura și implementarea funcționalităților cerute.

Bonus:

  • (10p) Aspect / funcționalitate deosebită (e.g., animații realtime, controale extra ale GPIO-urilor, arhitecturi de sistem elegante, utilizare de pachete/layers pe Buildroot sau Yocto etc.);
  • (10p) Optimizări deosebite de spațiu ale imaginii finale (cele mai mici 10 imagini primite, însă doar dintre cele care implementează toate task-urile!);

Imaginile care nu au au fost construite personal sau nu rulează deloc (cu excepția unor greșeli minore) nu vor fi punctate!

Precizări

  • Tema are deadline HARD (nu mai sunt admise soluții după expirare), așadar se recomandă să vă apucați din timp de rezolvarea acesteia!
  • :!: ATENȚIE: orice formă de plagiat nu va fi permisă și va duce la depunctare totală / restanță!
  • Pe Moodle găsiți și un forum ;)

Resurse

si/teme2025/tema2.txt · Last modified: 2025/12/14 17:16 by florin.stancu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0