logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

ESP32 i wyświetlacz dotykowy - tutorial cz. 3 - interakcje, gry i zabawy

p.kaczmarek2 05 Lip 2024 23:24 1650 0
REKLAMA
  • Ekran dotykowy ESP32-2432S028R z grą kółko i krzyżyk
    Dziś kontynuujemy przygodę z modułem ESP32 + wyświetlacz dotykowy w wersji ESP32-2432S028R. W tej części poćwiczymy interakcje z wyświetlaczem za pomocą ekranu dotykowego. W tym celu napiszemy tutaj kilka prostych, interaktywnych programów, takich jak mierzenie czasu reakcji użytkownika, quiz matematyczny oraz grę "kółko i krzyżyk".

    Na wstępie od razu chciałbym zaznaczyć, że nie jest to jedyny sposób rysowania na tym wyświetlaczu. Wręcz przeciwnie - jest nawet lepszy sposób, możemy na nim uruchomić LVGL i nawet tworzyć interfejsy w odpowiednim do tego programie, ale cierpliwości - LVGL będzie omawiany w kolejnych częściach.

    Poprzednie części:
    ESP32 i wyświetlacz dotykowy - tutorial - część 1 - jak programować? Podstawy
    ESP32 i wyświetlacz dotykowy - część 2 - jak rysować piksele, linie, kształty, kwestia wydajności

    Czas reakcji
    Na początek wymyśliłem coś bardzo prostego - pomiar czasu reakcji. Chodzi tu o czas reakcji użytkownika, tj. sprawdzamy jak szybko jesteśmy w stanie nacisnąć ekran po tym gdy zmieni on kolor.
    Zastanówmy się, jak możemy to zrealizować?
    Musimy w pętli:
    - ustawiać np. początkowy stan ekranu (powiedzmy czarny ekran)
    - czekać przez jakiś czas, dając użytkownikowi czas się przygotować
    - nagle zmienić np. kolory ekranu na przeciwne (dać użytkownikowi znak)
    - od tego momentu musimy jakoś zmierzyć czas do momentu naciśnięcia ekranu
    - potem trzeba ten czas wyświetlić
    Do pomiaru czasu użyjemy, tak jak to w Arduino bywa, funkcji millis.
    Teraz wpisujmy to kolejno w kod. Etap pierwszy - napis "WAIT" i oczekiwanie (można by je zrobić losowe):
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Etap drugi - sygnał do działania i początek pomiaru czasu:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Etap trzeci - pomiar czasu (docelowo pewnie można by zrobić przerwaniem, ale w takim prostym programie pozwoliłem sobie na pętle blokującą wykonanie wątku):
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Pętla przerywa się gdy ekran wykryje nacisk. Wtedy pobieramy bieżący czas, odejmujemy od niego zapisany i wyświetlamy wynik:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Prezentacja:



    No i pełny kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod




    Quiz Matematyczny
    W poprzednim programie wykorzystaliśmy podstawowe funkcje rysowania (wyświetlanie tekstu) oraz pobierania wejścia od użytkownika (wykrycie dotyku - tam tylko obecności, true lub false). Teraz pora to nieco rozwinąć. Postawmy sobie dwa zadania:
    - chcemy wyświetlać na ekranie nieco więcej (jakieś kształty, może nawet przyciski?)
    - chcemy też wykorzystać pozycje nacisku, być może by sprawdzić który z przycisków jest wciśnięty
    Docelowo będziemy korzystać raczej z LVGL, tam są gotowe przyciski i inne komponenty interfejsu użytkownika, ale na razie ćwiczymy proste programy dla nauki i satysfakcji.
    Dobrym przykładem takiej aplikacji będzie quiz matematyczny.
    Działanie będzie obejmować (tak jak wcześniej, w pętli):
    - wylosowanie dwóch liczb do operacji (np. mnożenia) i wyświetlenie ich na ekranie
    - przygotowanie odpowiedzi A B C D, gdzie jedna z nich jest poprawna a pozostałe odpowiedzi są różne od poprawnej (trzeba będzie losować w pętli - dopóki
    - oczekiwanie na wejście od użytkownika (nacisk)
    - sprawdzenie który z przycisków został wciśnięty (zrobimy to jakoś prosto, kilka warunków na sztywno)
    - sprawdzenie czy została wybrana poprawna odpowiedź
    - wyświetlenie odpowiedniego komunikatu w zależności od tego co wybrał użytkownik
    Można by też dodać zliczanie ilości poprawnych i złych odpowiedzi.

    To zaczynamy, zmienne globalne do zliczania:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Potem całość naszej nowej akcji realizujemy w loop, czyli w pętli. Najpierw czyszczenie ekranu i wyświetlenie bieżącego wyniku.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Trzeba też wylosować jakieś dwie liczby, powiedzmy od 1 do 10, dla wygody.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Potem wylosujmy operację do wykonania na tych liczbach. Będzie urozmaicenie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Teraz nieco sprawa się komplikuje. Musimy losować pozycje poprawnej odpowiedzi, bo co to by był za quiz, gdzie zawsze poprawną odpowiedzią jest np. A?
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższy kod najpierw losuje pozycję poprawnej odpowiedzi a potem losuje koniecznie niepoprawne liczby.
    Dalej możęmy już wyświetlić zadanie na ekranie oraz zaznaczyć strefy przycisków:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Tajemnicze "datum" określa sposób centrowania tekstu i zawiera następujące opcje:
    
    TL_DATUM = 0 = Top left
    TC_DATUM = 1 = Top centre
    TR_DATUM = 2 = Top right
    ML_DATUM = 3 = Middle left
    MC_DATUM = 4 = Middle centre
    MR_DATUM = 5 = Middle right
    BL_DATUM = 6 = Bottom left
    BC_DATUM = 7 = Bottom centre
    BR_DATUM = 8 = Bottom right
    

    MC to skrót od "middle centre", czyli centrujemy tekst.
    Najważniejsze jest jednak rysowanie "przycisków". Zakładamy, że przyciski zaczynają się w połowie ekranu, stąd zmienna baseY. Potem w pętli od 0 do 4 (bez 4, czyli tylko 0, 1, 2 i 3), za pomocą reszty z dzielenia (modulo) i dzielenia wybieramy jedną z ćwiartek naszego ekranu. Rysujemy kolejno prostokąty, a potem wyświetlamy w nich tekst.
    Potem czekamy na reakcję użytkownika:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższy kod oczekuje na dotyk ekranu a potem oblicza pozycję dotknięcia w pikselach wyświetlacza.
    Potem, trochę na piechotę, sprawdzamy która z ćwiartek została "dotknięta". Są tylko cztery, więc wystarczą trzy bloki warunkowe:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Ważne by pamiętać o aktualizowaniu na raz rysowania przycisków oraz ich sprawdzania, bo tutaj zasadniczo ten sam kształt powtarza się dwa razy. Docelowo można by to zrobić lepiej (a nawet wprowadzić klasę przycisku).
    Teraz zostaje sprawdzić czy użytkownik wybrał poprawną odpowiedź i wyświetlić odpowiedni komunikat:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Tutaj też jest zliczanie ilości odpowiedzi poprawnych oraz błędnych. Dodatkowo tutaj dodałem sobie wyświetlanie wybranej opcji, to tylko w celu weryfikacji działania programu.
    Po tym w kodzie jest tylko jeszcze jeden delay, aby dać użytkownikowi czas na odczytanie komunikatu.
    Oto cały kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Rezultat:






    Kółko i krzyżyk
    W poprzednim akapicie już nieco poćwiczyliśmy interakcje z czymś więcej niż tekstem, były cztery interaktywne przyciski, to może teraz zrobić w pełni projekt opierający się na "przyciskach" i kształtach? Jednym z popularniejszych zadań, chociażby na studiach, jest zrobienie gry kółko i krzyżyk. Spróbujmy to tutaj zrealizować. Weźmy tę łatwiejszą wersję, czyli grę dwóch graczy ze sobą. Nie chcę się skupiać tutaj na grze z komputerem.
    Ok, co potrzeba do tej gry?
    Na pewno:
    - rysowanie wizualnie planszy (z linii?)
    - rysowanie wizualnie kół i krzyżyków (do koła jest gotowa funkcja, a krzyżyk to tylko dwie linie, nie ma problemów)
    - trzeba przechowywać jakoś stan planszy, plansza jest dwuwymiarowa (szachownica), więc może tablica dwuwymiarowa? Tylko jaka jej zawartość - może enumerator (pole), chociaż po co? Może użyjemy znaków ASCII, czyli spacji oraz liter x i o?
    - potrzebna jest logika tur i sprawdzania wygranej, ale ona wcale nie jest taka trudna, mamy raptem dwie przekątne, oraz po 3 linie w pionie i poziomie. Te linie można by pętlami robić, więc dwie pętle i dwa warunki..

    Więc zacznijmy od kodu. Najpierw stałe, rozmiar planszy (ilość pól) oraz komórki (w pikselach). Potem wspomniana tablica, na początek pusta. Potem zmienna określająca czyj jest ruch.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Teraz rysowanie linii. Tutaj rysujemy pustą planszę, bez znaczków.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższy kod korzysta z pewnej małej optymalizacji, a mianowicie zamiast rysować "zwykłą" linie, korzystamy z funkcji która szybko rysuje linię wedle danej osi, osobno H - horizontal, oraz V - vertical.
    Teraz może wspomniane rysowanie znaku krzyżka (x):
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Jako argumenty podają tutaj indeksy kolumny i wiersza pola na szachownicy, dopiero w funkcji zamieniam je na piksele.
    Teraz analogicznie, rysowanie znaku kółka:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Teraz może pomocnicza funkcja która rysuje symbol z danej pozycji na szachownicy:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Teraz pomocnicza funkcja do stawiania znaczku (zmienia też kolejkę gracza):
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższa funkcja też sprawdza czy można postawić znak na danym polu, w przeciwnym razie nic nie jest wykonywane.
    Teraz czyszczenie planszy:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Zostało najgorsze -sprawdzenie warunku zwycięstwa.
    Trochę tu opcji jest.
    Linie poziome, pionowe, oraz dwa skosy.
    Zacznijmy od linii poziomych i pionowych - tu pomoże nam pętla.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższy kod można zoptymalizować (na raz sprawdzać obie osie).
    To teraz sprawdźmy przekątne:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższy kod czytelnik też może przerobić w jeden blok warunkowy, wystarczy nieco poćwiczyć warunki i operatory logiczne.

    Zostało sprawdzić jeszcze jedno - czy jest remis. Kiedy jest remis? Jak nie ma już miejsc na kolejny znaczek. Czyli sprawdzamy, czy w tablicy jest chociaż jeden znak spacji (wedle przyjętego standardu).
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Została funkcja setup i loop - ale tam można się domyśleć co dajemy. Gra będzie oczekiwała na wejście od użytkownika i tylko wtedy będzie się aktualizować:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Mapowanie pikseli na nasze komórki też jest proste - po prostu dzielimy na cellSize (operacje wykonujemy tam na liczbach całkowitych).

    Rezultat:




    Cały kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod



    Podsumowanie
    Ktoś by mógł zapytać - "czy naprawdę trzeba rysować wszystko na piechotę?". Oczywiście, że nie - ale warto tego spróbować, by mieć jakieś pojęcie o tym jak działają chociażby współrzędne, albo o tym, skąd wiemy która część interfejsu jest naciśnięta.
    Mimo to, w dalszych częściach przygody z ESP32-2432S028R będę już od tego odchodzić. Kolejne projekty które zaprezentuję (w tym np. sterowanie oświetleniem przez WiFi bądź odczyty pomiarów z Tasmoty) będę już opierać na LVGL, tak aby nie wynajdywać koła na nowa.
    Dodatkowo, w następnej części spróbujemy wykorzystać już łączność WiFi - pokażę jak pobrać jakieś dane z Internetu. Może prognozę pogody poprzez API? Szczegóły wkrótce.

    Fajne? Ranking DIY
    Pomogłem? Kup mi kawę.
    O autorze
    p.kaczmarek2
    Moderator Smart Home
    Offline 
  • REKLAMA
REKLAMA