Lekce 7 - Čtení uživatelských vstupů v Arduinu
V předchozím kvízu, Kvíz - Datové typy, podmínky a cykly v Arduinu, jsme si ověřili nabyté zkušenosti z předchozích lekcí.
V dnešní lekci Arduino tutoriálu se budeme zabývat tématem čtení vstupů zadaných uživatelem. Čtení vstupu od uživatelů je jednou z klíčových funkcí v programování pro platformu Arduino. Umožňuje nám reagovat na akce uživatelů. Ti mohou takto například rozsvěcet LED diody, nastavovat jim intenzitu jasu a podobně regulovat různé komponenty.
V tomto článku si ukážeme různé způsoby, jak číst vstupy od uživatelů. Uvedeme si také konkrétní příklady, jak jednotlivé funkce pro čtení implementovat do našeho kódu.
Funkce Serial.available()
Představme si nejdříve funkci Serial.available()
. Pomocí
této funkce získáme informaci o počtu bajtů, které jsou k dispozici ke
čtení v přijímacím bufferu sériového portu. Můžeme ji tedy použít k
ověření, zda jsou k dispozici data k přečtení. Další funkce tak budeme
volat až poté, co uživatel pošle data ke čtení.
Funkce Serial.available()
vrací celé číslo
odpovídající počtu bajtů, jež máme k dispozici. Pokud je v bufferu 10
bajtů, vrátí nám hodnotu 10
a podobně. Dokud uživatel nic
nezadá, vrací funkce hodnotu 0
.
Funkci Serial.available()
použijeme v kombinaci s
cyklem while
nebo s podmínkou, abychom ověřili, zda jsou k
dispozici data k přečtení. Teprve poté se pustíme do samotného čtení
dat.
Použití
Serial.available()
s while
cyklem
Příklad využití funkce Serial.available()
s
while
cyklem vypadá takto:
byte hodnota_vstupu; void setup() { Serial.begin(9600); } void loop() { while (Serial.available() > 0) { hodnota_vstupu = Serial.read(); // Nyní s načtenou hodnotu můžeme dále pracovat, my si ji necháme vypsat. Serial.println(hodnota_vstupu); } }
Pomocí while
cyklu zjišťujeme, zda je hodnota funkce
Serial.available()
vyšší než 0
. Pokud ano,
přikročíme ke čtení dat a jejich zpracování v těle cyklu.
Použití
Serial.available()
s podmínkou
Použití podmínky je podobné. Funkce loop()
poté vypadá
takto:
void loop() { if (Serial.available() > 0) { hodnota_vstupu = Serial.read(); // Nyní s načtenou hodnotu můžeme dále pracovat, my si ji necháme vypsat. Serial.println(hodnota_vstupu); } }
Pro čtení dat z uživatelského existuje několik funkcí.
V ukázkách výše jsme použili funkci Serial.read()
. K dispozici
máme ale i další funkce. Všechny se používají pro čtení, avšak je mezi
nimi rozdíl. Ten spočívá v tom, jaký typ dat chceme číst
a jakým způsobem jsou tato data zpracována. Pojďme si je
postupně ukázat a popsat si jejich specifika.
Funkce Serial.readString()
Pro práci s textovými řetězci využijeme funkci
Serial.readString()
. Funkce nám dobře poslouží, jestliže
očekáváme na vstupu od uživatele zadaný text.
Ukázkový kód s implementací této funkce vypadá následovně:
String text_vstup; // Vytvoříme si proměnnou typu String pro uložení textového řetězce. void setup() { Serial.begin(9600); // Nastavíme sériový monitor. } void loop() { if (Serial.available() > 0) // Zkontrolujeme, zda se v bufferu nacházejí data. { text_vstup = Serial.readString(); // Přečteme data a uložíme je do naší proměnné. Serial.print("Vase zprava: "); Serial.println(text_vstup); // Vypíšeme získaná data do sériového monitoru. } }
Pozor! Vývojové prostředí Tinkercad, které v kurzu používáme, nepodporuje českou diakritiku. Texty je tedy potřeba psát bez háčků a čárek a dalších speciálních znaků.
Funkce Serial.read()
Přejděme k funkci Serial.read()
. Tato funkce čte
jeden bajt dat z bufferu sériového portu a vrací ho
jako celé číslo. Postupně tedy načítá data ze sériového portu
a vrací ASCII hodnotu pro každý zadaný znak. Funkci
Serial.read()
můžeme použít ke čtení libovolného
typu dat.
Upravme náš ukázkový příklad a nechme si vypsat hodnoty získané
pomocí funkce Serial.read()
:
byte znak_vstup; // Vytvoříme si proměnnou typu byte pro uložení znaku. void setup() { Serial.begin(9600); } void loop() { if (Serial.available() > 0) // Zkontrolujeme, zda se v bufferu nacházejí data. { znak_vstup = Serial.read(); // Načteme zadaný znak a uložíme ho do naší proměnné. Serial.print("ASCII hodnota vlozeneho znaku: "); Serial.println(znak_vstup); // Vypíšeme číslo odpovídající hodnotě znaku v ASCII tabulce. } }
Zadáme-li například písmeno a
, vypíše se nám hodnota
97
. To je hodnota znaku a
v ASCII kódování.
Zadáním písmena b
získáme 98
atd. Zkusme si v
příkladu zadat delší text, např. ahoj
. Vidíme, že funkce
Serial.read()
projde postupně všechny znaky a vypíše pod sebe
jejich ASCII hodnoty.
Kdybychom pomocí funkce Serial.read()
načítali
číselnou hodnotu, získáme opět její ASCII kód. Číslo 1
má
hodnotu 49
atd.
Funkce Serial.parseInt()
Ukažme si nyní zpracování celočíselných hodnot načítaných ze
sériového portu. K tomu nám nejlépe poslouží funkce
Serial.parseInt()
. Funkce čte bajty z bufferu, dokud nenarazí na
konec čísla nebo na jiný nečíselný znak (např. mezeru nebo koncový znak
řetězce).
Načítání celých čísel pak vypadá následovně:
int cislo_vstup; void setup() { Serial.begin(9600); } void loop() { if (Serial.available() > 0) { cislo_vstup = Serial.parseInt(); Serial.print("Vlozene cislo: "); Serial.println(cislo_vstup); } }
Jako datový typ naší proměnné jsme použili int
. Zkusme si
jej v kódu přepsat na byte
. Víme, že proměnné typu
byte
mohou nabývat hodnot 0
až 255
.
Když nyní zadáme -2
, překročíme limit typu byte
a na sériovém monitoru se nám vypíše 254
. Pokud funkci
Serial.parseInt()
zkusíme poslat znak nebo celý textový
řetězec, vrátí nám hodnotu 0
.
Parsování vstupu
Funkce Serial.parseInt()
, kterou jsme si popsali výše, je
součástí knihovny Serial
pro Arduino. Tuto funkci lze volat
přímo na objektu Serial
a automaticky převádí
řetězec v bufferu na celé číslo. Převodu datových typů se
říká parsování a tento termín figuruje již v názvu
funkce.
Pro parsování existuje ale také metoda toInt()
. Tu využijeme
kdykoliv budeme potřebovat převést textový řetězec na celé číslo. Tuto
metodu voláme přímo na objektu typu String
.
Stejného výsledku jako v předchozím příkladu, dosáhneme s použitím
metody toInt()
následovně:
String vstup; int cislo; void setup() { Serial.begin(9600); } void loop() { if (Serial.available() > 0) { vstup = Serial.readString(); cislo = vstup.toInt(); Serial.print("Vlozene cislo: "); Serial.println(cislo); } }
Zadaný vstup jsme tedy nejdříve načetli a uložili jako textový
řetězec funkcí readString()
. Poté jsme do proměnné typu
int
uložili parsovaný vstup. Výsledek jsme si opět nechali
vypsat na sériový monitor.
Ošetření vstupů
Zastavme se ještě u tématu, které se čtením vstupů úzce souvisí. V našem programu je velice důležité myslet i na ošetření nesprávně zadaných hodnot. Jedná se například o případ, kdy vyžadujeme číslo a uživatel nám zadá text. V naší aplikaci ho pak na to musíme upozornit, aby se program choval předvídatelně.
K ošetření vstupů využijeme podmínky, případně konstrukci
switch
.
Pro kontrolu, zda uživatel zadal číslo, upravíme předchozí kód například takto:
String vstup; int cislo; void setup() { Serial.begin(9600); } void loop() { if (Serial.available() > 0) { vstup = Serial.readString(); cislo = vstup.toInt(); if (cislo != 0) { Serial.print("Vlozene cislo: "); Serial.println(cislo); } else { Serial.println("Nespravna hodnota, zadej znovu."); } } }
Pokud se nám podaří načíst číselnou hodnotu, necháme ji vypsat na sériový monitor. Jestliže uživatel zadá nečíselný znak, vyzveme jej k zadání nového vstupu.
V následujícím cvičení, Řešené úlohy k 6.-7. lekci programovacího jazyka Arduina, si procvičíme nabyté zkušenosti z předchozích lekcí.