12. díl - Arduino - Jazyk 2

Hardware PC Arduino Arduino - Jazyk 2

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulém tutoriálu o Arduinu jsme začali popisovat programovací jazyk. Dnešním dílem jeho popis dokončíme.

Funkce

Máme dvě základní funkce - setup() a loop(). Jak již víme, setup se vykoná jen jednou, loop ve smyčce. Jako příklad si vezměme situaci, kdy chceme zablikat diodou. Pokud budete otrocky ve všech případech vypisovat

...
digitalWrite(13, HIGH); // zabliká
delay(500);
digitalWrite(13, LOW);
delay(500);
digitalWrite(13, HIGH);
delay(500); ...
...
...
digitalWrite(13, HIGH); // zabliká
delay(500);
digitalWrite(13, LOW);
delay(500);
digitalWrite(13, HIGH);
delay(500); ...
...
...
digitalWrite... //zabliká

tak můžete, já vám v tom nebráním. Ale nebylo by jednodušší řešení? Samozřejmě, že je. Deklarujeme si vlastní funkci! Ta se deklaruje pomocí void, může brát i nějaké vstupní parametry a případně i vracet nějakou hodnotu.

void blikniPin13() //nastavíme blikání
{
    digitalWrite(13, HIGH);
    delay(500);
    digitalWrite(13, LOW);
    delay(500);
    digitalWrite(13, HIGH);
    delay(500);
    digitalWrite(13, LOW);
}
void loop()
{
    ...
    blikniPin13(); //zabliká
    ...
    ...
    blikniPin13();
    ...
    ...
    blikniPin13();
    ...
}

Není to jednoduší? A výhod je mnohem více. Pokud se rozhodnete, že LEDka nebude blikat po půl vteřině, ale po jedné vteřině, nemusíte v kódu hledat všechna místa, kde se bliká. Jen ve funkci přepíšete těch pár delay co tam jsou a rázem se změní úplně vše. A co kdybychom chtěli stejným způsobem klikat na více pinech? Nebudeme vypisovat funkce blinkiPin13, blikniPin12, blikniPin11, atd., ale dále funkci vstupní parametr:

void blik(int pin)
{
        digitalWrite(pin, HIGH);
        delay(500);
        digitalWrite(pin, LOW);
        ...
}
void loop()
{
        ...
        ...
        blikni(13); //blikání na pinu 13
        ...
        blikni(10); //blikání na pinu 10
        ...
}

Pokud se funkce dokončí, vrátí se kód na místo, kde se funkce zavolala, a pokračuje dál.

Pole

Omlouvám se zkušeným programátorům, vás teď budu chvíli nudit :) V minulém díle jsme dořešili proměnné. Co kdybychom potřebovali udělat databázi uživatelů? Co třeba zkusit něco takovéto:

String user1 = "pepa";
String user2 = "karel";
...

Určitě si domyslíte, že takhle by to nešlo. A co kdybychom měli třeba 500 uživatelů (u Arduina to moc nehrozí, ale jeden nikdy neví). K tomuto je lepší použít pole (ty jsme nakousli už v tomto díle ). Co to pole vůbec je? My si můžeme představit proměnnou jako kartičku se jménem uživatele. Pole si můžeme představit jako pořadač, ve kterém jsou kartičky narovnány do přihrádek. V tom jsou seřazeny podle čísel a můžeme je jednotlivě vyvolávat.

// databáze uživatelů
String uzivatele[5] = { "Pepa" , "David", "Franta", "Karel", "Michal" };
//v poli uzivatele je ulozeno 5 uzivatelu

není to jednodušší? Všimněte si, že před názvem musíme udat typ, který skladujeme. A jak vyvoláváme?

Serial.println(uzivatele[0]);

>>>Pepa

Serial.println(uzivatele[4]);

>>>Michal

Serial.println(uzivatele[1]);

>>>David

Protože programátoři začínají vždy od nuly, tak Pepa není pod indexem 1, ale pod indexem 0. Pod jedničkou je David, trojku má Franta... A co kdybychom chtěli kromě uživatelů ukládat i redaktory a administrátory? Je tu možnost pole uzivatele, pole redaktori, pole administratori, ale je i jednodušší možnost.

A v desátém díle jsme se setkali s pokročilejším polem - 2D polem. To si můžeme představit tak, že kartičky jsou v pořadačích a pořadače jsou umístěny do skříně. Opět zde použiji to pojmenování "pole polí", protože to je několik polí v poli. Vypadalo by to nějak takto:

String clenove[3][5] = {
        {"Honza", "Jenda", "Mirek", "Jirka", "user"}, //uzivatele
        {"Nikola", "Adam", "Zdenda"},   //redaktoři
        {"David", "Michal"},    //admini
};
//neberte ta jména osobně, je to jen příklad :)

Zas musíme napsat, co v poli ukládáme. V našem případě tam je string, takže String. Poté musíme do hranatých závorek napsat počet polí a maximální hodnotu, kterou každé pole pojme. Pokud bychom 5 snížili na 4, tak redaktoři a admini projdou, ale protože uživatelů je 5, tak to vyhodí chybu. A už si asi domyslíte vyvolání:

Serial.println(clenove[2][0]);

>>>David

Serial.println(clenove[0][3]);

>>>Jirka

To by byly pole, tak co tu máme dále?

Knihovny

Knihovny už jsme tak jednou nakousli a to v tomto díle, ale já je tu znovu připomenu, abychom tu měli všechno. Knihovny slouží k tomu, abychom nemuseli spoustu věcí složitě programovat, protože již to někdo dořešil za nás. Podrobněji se na ně podíváme v příštím díle, kde si i nějakou vytvoříme.

Escapování

Ve čtvrtém díle se objevil jeden nevysvětlený řádek:

Serial.println("Ajaj, asi sem ti nerozumel. Napis \"t\" pro teplotu a \"v\" pro vlhkost.");

>>> Ajaj, asi jsem ti nerozumel. Napis "t" pro teplotu a "v" pro vlhkost.

Co se tu stalo? Proč se nevypsala ta lomítka? A proč je to jeden řetězec, když tam je několik uvozovek? Řešení je jednoduché. Nastala nám zde situace, kde potřebujeme do stringu přidat uvozovky, ale uvozovky nám string ukončí. Co s tím? Použijeme tzv. escapování, kde se pomocí zpětného lomítka "zruší" význam následujícího znaku. Takže pokud chceme do stringu vypsat uvozovky, musíme napsat \". Co ale když chceme napsat zpětné lomítko? No, prostě ho odescapujeme - *\*.

Komentáře

Pokud si do kódu potřebujete něco poznamenat, nebo část deaktivovat bez mazání, existují komentáře. Máme dva typy - jednořádkové a více řádkové

// toto je jednořádkový komentář,
ale tato část se již bere jako kód

/* pokud chceme komentář na více
řádků, stačí použít
tento zápis */

Cykly

Pokud potřebujeme, aby se nám něco opakovalo, můžeme použít smyčku. Máme v podstatě dva typy - for a while. For je smyčka, která vykonává kód pro každou hodnotu řídící proměnné:

for (promena; podminka; akce)

Ve smyčce for nejdříve vytvoříme proměnnou. Poté za středníkem napíšeme podmínku, která musí platit, aby se smyčka vykonala. Za poslední středník pak přijde akce, která se po každém vykonání s proměnnou udělá. Pokud bychom třeba chtěli vypsat čísla od nuly do devíti, můžeme to udělat takto:

void setup()
{
        Serial.begin(9600);
        for (int i = 0; i < 10; i++) {
                Serial.println(i);
        }
}

void loop() {}

Otevření sériového portu známe. Pod ním je cyklus for. Nejdříve vytvoříme proměnnou int i s hodnotou nula. Podmínka je, že i bude menší než deset. Pokud bude rovno deseti, nebo větší (k čemuž tady nemůže dojít), tak se cyklus přestane vykonávat a kód přejde dál. Poslední je akce, která se s proměnnou vykoná, tudíž se k ní přičte jedna. Cyklus se tedy provede, když je v i nula, přičte se 1, i je jedna, znovu se provede a tak dále. A while?

While znamená dokud. Ten se hodí, pokud třeba chceme, aby při zmáčknutí tlačítka začala blikat LEDka a nepřestala dokud tlačítko neuvolníme.

while(digitalRead(10))
{
        // blikání
}

Tak dlouho, jak do pinu 10 bude proudit proud. Co ale kdybychom chtěli, aby se cyklus spustil při startu Arduina a neukončil se, dokud nestiskneme jedno tlačítko nebo druhé tlačítko? Šlo by to nějak zkombinovat do podmínky while, ale pokud by přibylo třetí tlačítko, pak čtvrté... Přece musí existovat lepší způsob, jak smyčku ukončit. Takže dejme tomu, že při spuštění Arduina se začne vykonávat cyklus, který bude něco dělat a ukončí se až při stisknutí jednoho, druhého, třetího nebo čtvrtého tlačítka bez nepřehledného zápisu v podmínce. Jak? Pomocí příkazu break, který cyklus ukončí.

...
while(true)     // nekonečná podmínka, ukončí se jen příkazem break,
{               // pokud k tomu nemáte pádný důvod, nepoužívejte jí
        ...
        if(digitalRead(10))
         break;
        if(digitalRead(11))
         break;
        ...

V nějakých situací to může být lepší než hromada podmínek v jednom řádku, neměli bychom konstrukci ovšem používat příliš často? To je snad všechno, v příštím dílu se podíváme na knihovny a jednu si i vytvoříme (snad).


 

 

Článek pro vás napsal Adam Ježek
Avatar
Jak se ti líbí článek?
5 hlasů
Autor se převážně věnuje Arduinu a psaní tutoriálů z této oblasti, občas napíše příležitostně nějakou tu zprávičku. Většinu svého volného času momentálně věnuje Linuxu a/nebo Raspberry Pi. Také umí C#, HTML, CSS, PHP a Python.
Miniatura
Předchozí článek
Arduino - Jazyk
Miniatura
Všechny články v sekci
Arduino
Miniatura
Následující článek
Arduino a SD karta
Aktivity (1)

 

 

Komentáře
Zobrazit starší komentáře (7)

Avatar
David Hart
Člen
Avatar
David Hart:2.2.2016 18:26

Ahoj, chci se zeptat, kolik místa v paměti RAM "sežere" deklarace proměnné:
String nejakyText;
Do proměnné nejakyText mohu posléze vložit hodnotu nejakyText="ok"; nebo i nejakyText = "Dobry den, jak se mate?";
Jde mi to to, jak šetřit RAM, když vím, že maximální délka obsahu proměnné typu string bude například 20 znaků. Nebo dochází k dynamické alokaci - dle momentální obsazenosti? Rád bych využíval string, protože často používám
xx=nejakyText­.substring(1,5); a podobně.
Díky David

 
Odpovědět 2.2.2016 18:26
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na David Hart
ostrozan:3.2.2016 0:07

Nejsem si úplně jistý, jak je to v arduino IDE, ale ve všech ostatních pro osmibitové procesory jako je Atmel AVR v arduinu se deklarovaný string bere jako konstanta a je uložený ve flash - je to hlavně z důvodu, že mikrokontroler má malou RAM a rychle bys ji zahnojil právě stringama.
A pokud to IDE nedělá automaticky, tak to udělej ty - const string

Jinak deklarace proměnné ti nesežere nic - to až teprve definice :)

 
Odpovědět 3.2.2016 0:07
Avatar
David Hart
Člen
Avatar
David Hart:3.2.2016 8:10

Ahoj, díky za odpověď, ale nerozuměli jsme si. Nepotřebuji uložit textovou konstatntu, ale pracovat s proměnnou typu String, o které vím, že nebude delší jak 10 znaků. Pokud se místo zabírá až při vlastním přiřazení NejakyText="Naz­dar"; a tudíž mi tato proměnná zabere pouze 6 bytů, není to problém. Pouze si musím ohlídat, aby při vlastním běhu programu nikdy nenastala situace, kdy do proměnných String dám opravdu dlouhé řetězce a RAM dojde.
Díky David

 
Odpovědět 3.2.2016 8:10
Avatar
jadana
Člen
Avatar
Odpovídá na David Hart
jadana:20.12.2016 23:10

sice budu reagovat na rok starý dotaz, ale třeba to někomu jinému pomůže.
bohužel se pamět dynamicky nealokuje, ba právě naopak.
Pokud použiješ nejdříve kratší string, a poté delší, alokuje se nové místo pro ten delší string, a to původní se už nepoužije, čímž vzniká něco podobného, jako je fragmentace na disku.
Takže je skutečně dobré si alokovat nejdříve prostor pro nejdelší použitý string, pak nebude paměť zbytečně fragmentovat a nedojde tak rychle.

No snad jsem to popsal tak, že to bude k pochopení...

 
Odpovědět 20.12.2016 23:10
Avatar
Jiří z Pardubic:15. ledna 22:05

Ahoj a hezký večer,
mám problém se čtením bytu. Zatím jsem nenašel řešení této jednoduché operace.

#include <SoftwareSerial.h>
byte z[] = {2, 3, 4, 5, 6, 7, 8, 9}; //defin. D2 az D9
int vysledek = 0; //výsledek cteni z D2 az D9 ...a to mi nejde!!!!!

void setup() {
Serial.begin(9600); //pro ladeni
for (int i = 2; i < 10; i++)
{
pinMode(z[i], INPUT);
}
}

void loop() { // z[i] potřebuji dostat do int vysledek
for (int i = 2; i < 10; i++)
digitalRead(z[i]);
}

Poradí mi někde? Děkuji.Jirka

 
Odpovědět 15. ledna 22:05
Avatar
Adam Ježek
Tým ITnetwork
Avatar
Odpovídá na Jiří z Pardubic
Adam Ježek:15. ledna 22:07
vysledek = digitalRead(z[i]);
Odpovědět 15. ledna 22:07
Pokud chceš odpovědět, klikni na odpovědět. Pokud chceš vložit zdroják, klikni na vložit zdroják (</>)
Avatar
Odpovídá na Adam Ježek
Jiří z Pardubic:15. ledna 22:19

Děkuji, tušil jsem, že to bude jednoduché. Mám pouze Průvodce světem Arduina a tam jsem to nenašel.
Příjemný večer. Jirka

 
Odpovědět 15. ledna 22:19
Avatar
Robert Neugebauer:27. února 14:28

Ahoj, mám za sebou pár základních projektů s Arduinem, ale není mi jasná jedna asi základní věc. Příklad: chci na základě stisku daného tlačítka rozsvítit/zhasnout danou diodu. V první části programu si otestuji tlačítka a v druhé části dle výsledku rozsvítím či zhasnu LEDky. Ale co když potřebuji, aby každá ledka po stisku svítila jinak dlouho (2,5,10 s) a pak vypla. Když dám do programu k LEDce dám příkaz "delay" tak se celý program zastaví a tím pádem netestuje tlačítka a neovládám ostatní LEDky. Jak to tedy obecně dělat? Děkuji

Editováno 27. února 14:29
 
Odpovědět 27. února 14:28
Avatar
Adam Ježek
Tým ITnetwork
Avatar
Odpovídá na Robert Neugebauer
Adam Ježek:27. února 15:00

Vytvoř si proměnnou typu long pro každou ledku, nebo to dej do pole. Při rozsvícení LEDky si do proměnné ulož aktuální čas - vrátí ti ho funkce millis(). Pak v loopu jenom zkontroluješ, jestli uložená hodnota + počet sekund * 1000 je větší než to co aktuálně vrací millis(). Pokud ano, už uplynulo dost sekund od rozsvícení a můžeš zhasnout.

Odpovědět 27. února 15:00
Pokud chceš odpovědět, klikni na odpovědět. Pokud chceš vložit zdroják, klikni na vložit zdroják (</>)
Avatar
Robert Neugebauer:27. února 15:43

Aha, super. Děkuji moc. Chápu -li to správně, když budu mít nějaký složitější projekt (ovládání robota tlačítky + kontrola čidel, koncáků apod.), tak obecně nejdříve v první části programu načíst vstupy a v druhé nastavit výstupy a tak pořád dokola, protože možnost aby něco běželo současně Arduino nemá. Jako třeba nějaký podprogram "na pozadí". To je možné například externími zařízením, jako je třeba Audio Shield, který umí přehrávat MP3jku nezávisle na Arduinu.

 
Odpovědět 27. února 15:43
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 17. Zobrazit vše