Funkce delay (pro zadávání zpoždění) je jednou z prvních věcí, které se naučíte, když se seznámíte s Arduinem. Jejich provoz však může způsobit mnoho problémů.
Naštěstí existuje sofistikovanější řešení založené na funkci millis. Umožňuje Arduinu provádět několik úloh „současně“.
Objednejte si sadu prvků a začněte se učit v praxi! Kliknutím sem přejdete do obchodu >>
Problém multitaskingu Arduino
Nejběžnějším příkladem pro začátek výuky s Arduino je blikání LED diody. Obvykle to vypadá takto:
#define LED1pin 3
void setup() {
//Pin, ke kterému je dioda připojena jako výstup
pinMode(LED1pin, OUTPUT);
}
void loop() {
digitalWrite(LED1pin, HIGH); //Zapni diodu
delay(1000); //Počkej 1000 ms
digitalWrite(LED1pin, LOW); //Vypni diodu
delay(1000); //Počkej 1000 ms
}
Krátký obsah smyčky loop() stačí k tomu, aby LED dioda začala blikat. Poznámka: Přechod z nastavení příslušného stavu na výstupu do funkce delay() je okamžitý, v animaci níže je vidět, co se děje, až po „malém okamžiku“:
Mnoho lidí si okamžitě představí blikání dvou LED diod (s různou frekvencí). Tím se dostanete do následujícího programu:
#define LED1pin 3
#define LED2pin 4
void setup() {
//Pin, ke kterému je dioda připojena jako výstup
pinMode(LED1pin, OUTPUT);
pinMode(LED2pin, OUTPUT);
}
void loop() {
digitalWrite(LED1pin, HIGH); //Zapni diodu
delay(1000); //Počkej 1000 ms
digitalWrite(LED1pin, LOW); //Vypni diodu
delay(1000); //Počkej 1000 ms
digitalWrite(LED2pin, HIGH); //Zapni diodu
delay(500); //Počkej 500 ms
digitalWrite(LED2pin, LOW); //Vypni diodu
delay(500); //Počkej 500 ms
}
Způsob, jakým tento kód funguje, se liší od toho, co většina lidí očekává. Je důležité si uvědomit, že Arduino všechny instrukce ve smyčce loop() provádí řádek po řádku. Mikroprocesor může v jednom okamžiku provádět pouze jednu operaci.
Je třeba také poznamenat, že funkce delay() zcela zastaví běžící program!
Místo očekávaného současného blikání diod LED se objeví něco zvláštního:
Na první pohled se to může zdát překvapivé. Vždyť počítače, které používáme, provádějí velké množství různých operací současně (dokonce i ty s jednojádrovým procesorem). V tomto případě není procesor počítače ve skutečnosti o nic lepší než mikrokontrolér Arduino. Také provádí vždy jen jednu operaci. Víceúlohový charakter počítačů spočívá v tom, že velmi rychle přepínají mezi různými úlohami (tisíckrát za sekundu). Z lidského pohledu to není patrné.
Účinek je srovnatelný s multiplexování sedmisegmentových displejůTam se vždy rozsvítí pouze jedna číslice, zbytek je způsoben setrvačností našich očí.
Kdy lze použít funkci delay()?
Funkce delay() je velmi jednoduchá a praktická. Bohužel každý výskyt funkce zablokuje celý program. U jednoduchých příkladů se nejedná o zásadní problém. U velkých projektů je to však problematické. Použití této funkce může vést k obtížně zjistitelným chybám programu (např. dočasné problémy s některými knihovnami).
Funkci delay() lze použít pouze tehdy, pokud jste si neustále vědomi, že jejím úkolem je „zmrazit“ provádění celého programu!
Co místo funkce delay()?
Existuje řada knihoven, které umožňují zavést „magické“ neblokující zpoždění. Samozřejmě byste si měli usnadnit život a používat takové hotové knihovny. Mnoho lidí však neví, jak tato zpoždění fungují, což vede k dalším, ještě podivnějším chybám.
Proto je dobré začít o programech přemýšlet a psát je jinak. Proto je nejlepší začít od nuly, bez prefabrikovaných knihoven…..
Stopky zabudované do Arduino - millis()!
V Arduino najdeme funkci millis(). Jeho způsob fungování lze nejlépe přirovnat ke stopkám, které se spustí po zapnutí Arduino. Tato funkce vrací počet milisekund, které uplynuly od zapojení desky. Tyto „stopky“ nemusíme spouštět, to za nás udělá Arduino. Funguje to vždy a v každém programu. Proces počítání je realizován hardwarově, tj. pomocí čítačů (časovačů). Údaje z funkce millis() nelze neúmyslně zfalšovat.
Ani funkce delay() nedokáže zastavit interní stopky, jejichž výsledek můžeme přečíst pomocí funkce millis()!
Kombinace obou zmíněných funkcí (delay a millis) se míjí účinkem a může způsobit určité problémy, ale je dobré vědět, že je to možné – ještě se k tomu vrátíme.
Jak používat funkci millis()?
unsigned long aktualniCas = millis();
Podívejme se nyní, jak tyto stopky fungují v praxi. Můžeme začít nastavením aktuálního času pomocí příkazu UART do počítače. Samozřejmě je nemůžeme posílat bez přerušení, protože jinak bychom zahltili datovou vyrovnávací paměť a způsobili pád všeho.
Zatím nevíme, jak program „chytře“ zastavit, a tak naposledy použijeme „nešťastnou“ funkci delay():
unsigned long aktualniCas = 0;
void setup(){
Serial.begin(9600);
}
void loop(){
// Získej počet milisekund od startu
aktualniCas = millis();
//Odešli do PC
Serial.println(aktualniCas);
// Počkej 1000 ms
delay(1000);
}
Jakmile je program spuštěn, na monitoru sériového rozhraní se přibližně každou sekundu zobrazuje počet milisekund, které uplynuly od spuštění Arduino:
Z nějakého důvodu jsem napsal „přibližně každou sekundu“, protože jak vidíte, dochází k drobným nepravidelnostem v rozmezí +/- 1 milisekundy. Mimochodem, tento experiment ukazuje další nevýhodu funkce delay(): V případě této metody není počítání času přesné. Samozřejmě, že 1 milisekunda zde mnoho nezmění. Pokud by však obvod běžel neustále (např. jako hodiny), byly by odchylky v čase značné.
Hotové sady pro kurzy Forbot
Komponenty pro cvičení z Kurzu Arduino (úroveň 2) jsou k dispozici jako hotové sady! Patří sem programovatelné diody, analogové a digitální teploměry, 7-segmentové displeje a senzor pohybu (PIR).
Jaké je omezení funkce millis()?
Jak vidíte na předchozí animaci, vrácená hodnota se rychle zvyšuje. V určitém okamžiku tento čítač přeteče (překročí svou maximální hodnotu a vrátí se na nulu). Naštěstí v tomto případě dochází k přetečení až po 50 dnech provozu.
To samozřejmě neznamená, že byste měli Arduino resetovat každých 50 dní. Počítadlo se přeteče a začne pracovat znovu od začátku. Pokud je program zapsán správně, nemá vynulování počítadla žádný vliv na jeho činnost.
Měli byste také mít na paměti, že operace s takto velkými čísly (unsigned long) mohou vést k některým chybám a logickým odchylkám. Zvláště když se snažíme provádět matematické operace s menšími proměnnými (např. typu int)!
Lepší způsob měření času
Již víme, že v Arduino jsou zabudovány přesné stopky. Nyní je čas využít je k vytvoření zpoždění. Stopky nemůžeme vynulovat ani je nijak ovlivnit. Potřebujeme pouze znát aktuální čas ve vztahu ke startu Arduino.
Jak by mělo vypadat měření času pro sekundu?
- Zkontrolujeme aktuální čas a zapamatujeme si ho.
- V každém průchodu smyčkou kontrolujeme aktuální čas:
- Pokud je rozdíl mezi aktuálním časem a dříve uloženým časem menší než 1 sekunda, znamená to, že požadovaný čas ještě neuplynul.
- Pokud je rozdíl mezi uloženým časem a aktuálním časem 1 sekunda, pak ….. uplynulo právě tolik času!
Zásadní na tomto přístupu je, že program neustále „běží v kruhu“ a nikdy se nezastaví. Neustále kontroluje, kolik času uplynulo, a může současně provádět další činnosti.
Nyní je čas převést výše uvedený algoritmus do kódu:
unsigned long aktualniCas = 0;
unsigned long zapamatovanyCas = 0;
unsigned long rozdilvCase = 0;
void setup(){
Serial.begin(9600);
}
void loop(){
// Získej počet milisekund od začátku
aktualniCas = millis();
rozdilvCase = aktualniCas - zapamatovanyCas;
//Pokud je rozdíl větší než jedna sekunda
if (rozdilvCase >= 1000UL) {
//Zapamatuj si aktuální čas
zapamatovanyCas = aktualniCas;
//Odešli do PC
Serial.println(aktualniCas);
}
}
Proměnné, které uchovávají čas, jsou typu unsigned long. Aby se předešlo problémům při porovnávání hodnot, byl počet milisekund zapsán s příponou UL (1000UL).
Tím je zajištěno, že překladač bude s hodnotou 1000 zacházet jako s hodnotou typu unsigned long.
Program zkontroluje aktuální čas, spočítá rozdíl a pokud je větší nebo roven 1000, víme, že určitě uplynula sekunda. Pro jistotu je v podmínce místo pevného „== 1000“ nastavena nerovnost. U velmi složitých programů se může stát, že se z nějakého důvodu netrefíme do tisícovky dokonale. Tato nerovnost zajišťuje, že se program „nepřeruší“ a že podmínku zadáme při nejbližší příležitosti (např. 1001ms).
Víme, že sekunda uplynula, když je podmínka splněna. Proto si aktuální čas pamatujeme jako předchozí čas (v němž byla podmínka splněna) hned na začátku. Další sekundu počítáme od nové hodnoty. V tomto případě je podmínka splněna, když počítadlo milisekund opět zobrazí 1000, 2000, 3000, 4000 atd..
Níže se můžete podívat, jak program funguje v praxi:
Okamžitě vidíte výhodu oproti funkci delay(), zde je časový výstup volán dokonale každou sekundu!
Proměnná rozdilvCase je samozřejmě zbytečná (je zde použita z důvodu přehlednosti). Časový rozdíl můžete počítat také přímo v podmínce:
//Je-li rozdíl větší než jedna sekunda
if (aktualniCas - zapamatovanyCas >= 1000UL) {
Blikání diody bez delay()
Nyní je čas použít získané informace k záblesku diody. Čas můžeme měřit přesně, takže jde jen o to, aby se stav diody změnil ve správný okamžik. Připojíme diodu na pin číslo 3 a můžeme začít.
V příkladech používám dvě diody, aby byly na obrázcích lépe vidět. Pokud použijete diody z Arduino (úroveň I) můžete použít diody RGB ze sady úrovně II a ovládat každou barvu nezávisle.
Aby obvod správně fungoval, musíme uložit aktuální stav diody (zapnuto/vypnuto). Pak můžeme v podmínce, která platí každou sekundu, změnit stav diody na opačný. Tuto informaci uložíme do proměnné int stavLED1 = LOW;. Stav diody můžeme později negovat pomocí následující operace: stavLED1 = !stavLED1;
LOW je konstanta, která znamená 0, takže ji můžeme přiřadit proměnné typu int.
Kód, který tento úkol splňuje, je uveden níže:
#define LED1 3
int stavLED1 = LOW;
unsigned long aktualniCas = 0;
unsigned long zapamatovanyCasLED1 = 0;
void setup(){
Serial.begin(9600);
pinMode(LED1, OUTPUT);
}
void loop(){
// Získej počet milisekund od začátku
aktualniCas = millis();
//Je-li rozdíl větší než 1 sekunda
if ( aktualniCas - zapamatovanyCasLED1 >= 1000UL) {
//Zapamatuj aktualní čas
zapamatovanyCasLED1 = aktualniCas;
//Změníme stav diody na opačný
stavLED1 = !stavLED1;
//nastavit na diodě nový stav
digitalWrite(LED1, stavLED1);
}
}
Když je aktivován, bliká LED dioda jako v prvním příkladu:
Negace stavu ve tvaru stavLED1 = !stavLED1; je stručná a pohodlná, ale nemusí být pro každého intuitivní (funguje jen díky správné deklaraci konstant LOW a HIGH). Pro větší bezpečnost lze tento kus kódu přepsat do této podoby:
if (stavLED1 == LOW) {
stavLED1LED1 = HIGH
} else {
stavLED1 = LOW;
}
Multitasking na Arduino - blikající LED diody
Je čas rozšířit výše uvedený příklad tak, aby dvě LED diody blikaly nezávisle na sobě. Teprve pak se ukáže výhoda tohoto řešení. Tentokrát chceme, aby jedna dioda měnila svůj stav častěji než druhá:
Stále bude existovat pouze jedna smyčka loop() a všechny úlohy se budou provádět řádek po řádku. Tentokrát bude program schopen zajistit, aby dvě LED diody blikaly nezávisle na sobě.
Potřebujeme dvě další proměnné. První bude obsahovat informaci o tom, „kdy“ jsme naposledy změnili stav druhé diody. Další bude obsahovat informace o jeho stavu (zapnuto/vypnuto).
Zbytek programu probíhá stejným způsobem. Připojíme druhou diodu na pin 4 a můžeme začít:
#define LED1 3
#define LED2 4
int stavED1 = LOW
int stavLED2 = LOW;
unsigned long aktualniCas = 0;
unsigned long zapamatovanyCasLED1 = 0;
unsigned long zapamatovanyCasLED2 = 0;
void setup(){
Serial.begin(9600);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
}
void loop(){
// Získej počet milisekund od startu
aktualniCas = millis();
//Je-li rozdíl větší než 1 sekunda
if (aktualniCas - zapamatovanyCasLED1LED1 >= 1000UL) {
//Zapamatuj si aktuální čas
zapamatovanyCasLED1 = aktualniCas;
//Změníme stav diody na opačný
stavLED1 = !stavLED1;
//nastavíme nový stav diody
digitalWrite(LED1, stavLED1);
}
//Je-li rozdíl větší než 0,5 sekundy
if (aktualniCas - zapamatovanyCasLED2 >= 500UL) {
//Zapamatuj si aktuální čas
zapamatovanyCasLED2 = aktualniCas;
//Změníme stav diody na opačný
stavLED2 = !stavLED2;
//nastavíme nový stav diody
digitalWrite(LED2, stavLED2);
}
}
Od této chvíle blikají diody LED nezávisle na sobě! Jeden mění svůj stav každou sekundu a druhý každou půl sekundu:
Abyste snáze rozpoznali rozdíl, můžete na chvíli zakrýt jednu diodu a pak druhou.
Změny jsou nejvíce patrné, když nastavíme větší rozdíly. Nechte první LED diodu měnit svůj stav každou sekundu a druhou každých 200 ms. Nejlepší je přidat dvě proměnné, například: blikaniLED1 a blikaniLED2.
Nové proměnné deklarujeme jako unsigned long, čímž se vyhneme možným problémům při porovnávání velkých hodnot. V tomto případě již samozřejmě nemusíte k číslu přidávat zkratku UL.
#define LED1 3
#define LED2 4
int stavLED1 = LOW;
int stavLED2 = LOW;
unsigned long blikaniLED1 = 1000;
unsigned long blikaniLED2 = 200;
unsigned long aktualniCas = 0;
unsigned long zapamatovanyLED1 = 0;
unsigned long zapamatovanyLED2 = 0;
void setup(){
Serial.begin(9600);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
}
void loop(){
// Získej počet milisekund od startu
aktualniCas = millis();
//Pokud je rozdíl větší než blikaniLED1
if (aktualniCas - zapamatovanyCasLED1 >= blikaniLED1) {
//Zapamatuj si aktualniCas
zapamatovanyCasLED1 = aktualniCas;
//Změníme stav diody na opačný
stavLED1 = !stavLED1;
//nastavit nový stav diody
digitalWrite(LED1, stavLED1);
}
//Pokud je rozdíl větší než blikaniLED2
if (aktualniCas - zapamatovanyCasLED2 >= blikaniLED2) {
//Zapamatuj si aktualniCas
zapamatovanyCasLED2 = aktualniCas;
//Změníme stav diody na opačný
stavLED2 = !stavLED2;
//nastavíme nový stav diody
digitalWrite(LED2, stavLED2);
}
}
Efekt je nyní mnohem zřetelnější:
Blikající LED diody a tlačítko na Arduino
Ještě lepšího efektu dosáhneme, když do programu přidáme tlačítko. Pokud bychom použili funkci delay(), tato funkce by ve výchozím nastavení zablokovala možnost okamžité kontroly vstupu. Tlačítko musíte držet stisknuté, dokud program nedosáhne kontrolního řádku. To by fungovalo jen zřídka, protože funkce delay() by program na několik sekund zastavila.
Zde takový problém není, můžeme jednoduše přidat podmínku, která funguje okamžitě. Například stisknutí tlačítka (pin 2) by mělo způsobit mnohem rychlejší změnu stavu LED1 (každých 100 ms).
#define LED1 3
#define LED2 4
#define TASTE 2
int stavLED1 = LOW;
int stavLED2 = LOW;
unsigned long blikaniLED1 = 1000;
unsigned long blikaniLED2 = 200;
unsigned long aktualniCas = 0;
unsigned long zapamatovanyCasLED1 = 0;
unsigned long zapamatovanyCasLED2 = 0;
void setup(){
Serial.begin(9600);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(TASTE, INPUT_PULLUP);
}
void loop(){
// Získej počet milisekund od startu
aktualniCas = millis();
//Pokud je tlačítko stisknuto, blikej rychleji
if (digitalRead(TASTE) == LOW) {
blikaniLED1 = 100;
} else {
blikaniLED1 = 2000;
}
//Pokud je rozdíl větší než blikaniLED1
if (aktualniCas - zapamatovanyCasLED1 >= blikaniLED1) {
//Zapamatuj aktuální čas
zapamatovanyCasLED1 = aktualniCas;
//Změníme stav diody na opačný
stavLED1 = !stavLED1;
//nastavíme nový stav diody
digitalWrite(LED1, stavLED1);
}
//Pokud je rozdíl větší než blikaniLED2
if (aktualniCas - zapamatovanyCasLED2 >= blinkLED2) {
//Zapamatuj aktuální čas
zapamatovanyCasLED2 = aktualniCas;
//Změníme stav diody na opačný
stavLED2 = !stavLED2;
//nastavíme nový stav diody
digitalWrite(LED2, stavLED2);
}
}
A je to – jednoduchý multitasking v praxi! LED diody blikají nezávisle na sobě a reagují okamžitě po stisknutí tlačítka. To je samozřejmě jen jeden příklad. Místo změny stavu LED diod se tam může objevit i něco úplně jiného.
Inteligentní osvětlení
Původně měl být tento článek o něčem úplně jiném (o domácí automatizaci), takže toto téma použiji jako „velmi volný“ příklad na závěr.
Téma „smart home“ zajímá stále více lidí. Automatické zvedání žaluzií, dálkové ovládání spotřebičů a osvětlení, dálkové sledování teploty. Na trhu existuje mnoho hotových řešení pro domácí automatizaci. Většina těchto systémů má bohužel jednu společnou nežádoucí vlastnost – vysokou cenu.
Tentokrát se věnujeme tématu „inteligentního osvětlení“ pro osvětlení schodiště. Mezi čtenáři Forbotu jsou i mladí studenti elektroniky. Proto se nebudu zabývat řízením běžného osvětlení (230 V) – budeme se zabývat bezpečným příkladem. Pokud máte zájem o složitější řešení, můžete si jistě obvod sestavit sami (s relé).
Příklad jednoduché automatizace pomocí Arduino
Když jdeme večer dolů, vždycky rozsvítíme. Totéž platí, když procházíme tmavou chodbou. To je ideální úkol pro automatizaci! Předpokládejme tentokrát následující situaci: Přímo vedle vstupních dveří se nachází schodiště s malou chodbou dole a nahoře.
Předpokládejme, že chceme aktivovat světlo při detekci pohybu. Pokud senzor PIR zjistí, že se někdo nachází v blízkosti schodů, rozsvítí světlo na 180 sekund nebo dokud se neotevřou dveře.
Výběr správného osvětlení
Jak jsem již zmínil, zábavu s 230 V přenecháme zkušenějším uživatelům. Běžné osvětlení se obvykle ovládá pomocí relé.
Druhou, mnohem bezpečnější možností jsou v poslední době oblíbené LED pásky. Nejoblíbenější moduly tohoto typu lze bezpečně provozovat s nižším napětím (např. 12 V). K Arduino je lze připojit přes relé nebo tranzistor (MOSFET). Informace o ovládání periferních zařízení prostřednictvím MOSFETu byly popsány v části 3 kurzu.
Je třeba mít na paměti, že relé vylučuje možnost PWM regulace (řízení jasu LED pásků). Kromě toho je při každém zapnutí a vypnutí osvětlení slyšet spínací hluk relé.
Třetí možností je použití programovatelných diod, například WS2812, kterým jsme se věnovali ve druhé části kurzu. Toto řešení je dražší než běžné diody, ale umožňuje zajímavější efekty.
Pro testovací účely (zvolil jsem tuto možnost) můžete také připojit běžnou LED diodu, která simuluje osvětlení. Připojil jsem diodu na pin číslo 4.
Senzory
Nyní je čas vybrat senzory. Pohyb budeme samozřejmě detekovat pomocí PIR senzoru (pin číslo 5). Pro monitorování dveří je nejvhodnější jazýčkový spínač (pin číslo 3). Všechny tyto senzory byly popsány v článku o vytvoření jednoduchého poplachového centra.
V horní části chodby nainstalujeme PIR čidlo a na dveře jazýčkový spínač:
V praxi vypadala moje testovací platforma takto:
Program používající funkci millis()
Než jsme provedli cvičení v této části, zpozdili jsme světlo na 180 sekund pomocí delay() a zapneme jej. Při detekci pohybu se světlo a delay(180) zapne a během této doby je obvod „zamrzlý“. Nereaguje na jazýčkový spínač ani na jiné senzory…..
V popsaném případě by samozřejmě bylo možné pod přerušení připojit jazýčkový spínač. To by však s několika senzory nebylo možné!
Je tedy načase využít mechanismus, který jsme se dnes naučili. Program by měl fungovat následujícím způsobem: Při detekci pohybu se světlo aktivuje na 180 sekund. Po uplynutí této doby se světlo vypne (pokud nedojde k dalšímu pohybu) nebo pokud někdo opustí místnost, tj. otevře dveře.
Jeden z mnoha způsobů, jak tento příklad realizovat, je následující:
#define OSVĚTLENÍ 4
#define JAZÝČKOVÝ SPÍNAČ 3
#define PIR 5
unsigned long aktualniCas = 0;
unsigned long zapamatovanyCasOsvetleni = 0;
void setup(){
Serial.begin(9600);
pinMode(OSVĚTLENÍ, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP); //Jazýčkový spínač jako vstup
pinMode(PIR, INPUT); //PIR jako vstup
}
void loop(){
//Získej počet milisekund od startu
aktualniCas = millis();
//Pokud dveře otevřené
if (digitalRead(JAZÝČKOVÝ SPÍNAČ) == HIGH) {
digitalWrite(OSVĚTLENÍ, LOW); //Vypni osvětlení
} else if (digitalRead(PIR) == HIGH) {
//Pokud jsou dvě zavřené a je detekován pohyb
zapamatovanyCasOsvetleni = aktualniCas; //Zapamatuj čas
digitalWrite(OSVĚTLENÍ, HIGH); //Zapni osvětlení
}
//Pokud svítí po určitou dobu, vypněte světlo
if (aktualniCas - zapamatovanyCasOsvetleni >= 180000UL) {
digitalWrite(OSVĚTLENÍ, LOW);
}
}
Základní myšlenka tohoto příkladu má samozřejmě některé nevýhody a aby byl obvod použitelný v běžném životě, musel by být rozšířen. Přinejmenším by mělo smysl přidat fotorezistor, aby se světlo zapínalo pouze ve tmě. Další vývoj obvodu přenechávám zájemcům z řad hobbyistů.
Shrnutí
Poslední příklad byl velmi jednoduchý, ale ukazuje praktickou aplikaci pro funkce milliskterá hrála v této části hlavní roli. Doufám, že od nynějška už nikdo nebude mít problémy s funkcí delay, která blokuje celý program.
Rád bych vás povzbudil, abyste si udělali vlastní testy! Zde popsané programy je vhodné rozšířit. Přidejte další LED diody, tlačítka a další senzory. Pokud je vše jasné, můžete bez obav přejít na knihovny, které takové zpoždění „zázračně“ provedou samy.
Objednejte si sadu prvků a začněte se učit v praxi! Kliknutím sem přejdete do obchodu >>