Vyhledávání

#5 - Sériová komunikace a Cykly

05.01.2014 19:09

Sériová komunikace a Cykly

V dnešním díle splním sliby, které jsem v posledních článcích dal. Ukážeme si, jak může Arduino komunikovat přes sériovou linku s PC a vysvětlíme si, jak fungují cykly.


Sériová komunikace

Ve článku #3 - Základní struktury jazyka Wiring jsme si popsali, jak sériová komunikace funguje. Neřekli jsme si ale, k čemu se dá využít. Pokud chceme od Arduina získávat nějaké hodnoty, nebo mu je posílat, přichází na řadu právě sériová komunikace. Pracujeme-li například na projektu meteostanice, určitě se hodí získané hodnoty nějakým způsobem zpracovat. Nejlepší je posílat je do PC a tam je v nejjednodušším případě zobrazovat jako text, nebo je pomocí dalších programů zpracovávat. Poté přijde na řadu právě sériová komunikace. Ta je také důležitým nástrojem při ladění programů. Když nám něco nefunguje a my nevíme proč, může být užitečné si nechat vypisovat hodnotu proměnné, nebo určitý text pouze v podmínce, takže máme kontrolu nad tím, jaká část kódu se právě provádí. Dá se také využít při komunikaci Arduina a dalších zařízení (jiné Arduino, moduly atd.). K tomu slouží piny popsané Rx a Tx (u UNO odpovídají pinům 0 a 1). Všechny funkce využívající sériovou komunikaci obsahují slovo Serial. My si představíme pár nejužitečnějších z těchto funkcí.

K již zmíněnému čtení informací v textové podobě na PC se používá tzv. Serial monitor. Ikonu pro jeho spuštění nalezneme v IDE v horním panelu s ikonami úplně vpravo (ikona s lupou). Po spuštění Serial monitoru musíme ještě nastavit rychlost komunikace pomocí rolovací nabídky v pravé dolní části. Ukažme si nyní, jak může Arduino komunikovat s PC právě přes sériovou linku a Serial monitor.

Poznámka: Větší desky (Mega, Due...) mají k dispozici více kanálů pro sériovou komunikaci a připojení dalších zařízení. Piny se pak jmenují: Rx1, Tx1, Rx2, Tx2... Jejich použití je popsáno zde. S počítačem ale komunikuje pouze jedna základní linka.


Zahájení komunikace - Serial.begin()

Tato funkce se používá k zahájení sériové komunikace. Do závorek se píše parametr rychlosti této komunikace, který odpovídá počtu přenosů za sekundu. Při komunikaci s PC ale tato rychlost není pouze na našem výběru. Může mít jen několik vyhrazených hodnot (300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600 a 115200). Nejčastěji se používá hodnota 9600. Funkce Serial.begin() se většinou volá v části setup.

void setup(){
  Serial.begin(9600);
  
  Serial2.begin(9600); //pro Arduina s více sériovými linkami
}

 


Odeslání dat - Serial.print() a Serial.println()

Tyto funkce slouží k odeslání hodnoty z Arduina do PC, nebo jiného zařízení. Liší se od sebe tím, že funkce Serial.print() posílá data stále na jednom řádku. Funkce Serial.println() vždy na konci odeslaných dat přidá znak pro zalomení řádku. Rozdíl mezi těmito dvěma funkcemi můžete vidět na následujících příkladech.

//ukázka funkce Serial.print();
void setup(){
  Serial.begin(9600);
}

void loop(){
  Serial.print("abc ");
  delay(500);
}
//ukázka Serial.println();
void setup(){
  Serial.begin(9600);
}

void loop(){
  Serial.println("abc ");
  delay(500);
}

Při pohledu na použití těchto funkcí je jasné, že obě mají jeden povinný parametr, kterým je odesílaná hodnota. V praxi fungují tak, že se odesílá ASCII kód odesílaných znaků, který se poté v PC patřičně interpretuje. V ASCII tabulce znaků má malé 'a' v desítkové soustavě hodnotu 97. Při volání funkce Serial.print('a') se tedy odesílá číslo 97 a až v PC dojde k jeho překladu. Serial.print() a Serial.println() mají ale ještě jeden nepovinný parametr. Ten může udávat buď typ odesílaných dat (v jaké soustavě se číslo ve výpisu zobrazí - DEC = desítková, OCT = osmičková, HEX = šestnáctková a BIN = dvojková), nebo počet desetinných míst (což je použitelné u datového typu float). Pokud u typu float neuvedeme počet desetinných míst, automaticky se odesílají pouze dvě místa. Pokud je to potřeba, čísla se zaokrouhlí.

Poznámka: O číselných soustavách si můžete více přečíst na české Wikipedii

//použití jednoho parametru
Serial.print(97); //vypíše: 97 
Serial.print(2.123456); //vypíše: 2.12
Serial.print('a'); //vypíše: "a"
Serial.print("ABCDEFGHIJ"); //vypíše: "ABCDEFGHIJ"

//použití nepovinného operátoru pro typ dat
Serial.print(97, DEC); //vrátí: 97
Serial.print(97, BIN); //vrátí: 1100001 (což je 97 ve dvojkové soustavě)
Serial.print(97, OCT); //vrátí: 141 (=97 v osmičkové soustavě)
Serial.print(97, HEX); //vrátí: 61 (=97 v šestnáctkové soustavě)

//použití nepovinného operátoru pro délku čísel
Serial.print(4.56789, 0); //vrátí: 5
Serial.print(4.56789, 1); //vrátí: 4.6
Serial.print(4.56789, 3); //vrátí: 4.568

Před chvílí jsem zmínil, že i znaky jsou přenášeny jako číslo, které jim odpovídá v ASCII tabulce znaků. Dá se s nimi tedy pracovat stejně, jako s čísly. Následující kód přenáší znak 'a', kterému odpovídá hodnota 97 v různých soustavách.

Serial.print('a', DEC); //vrátí: 97
Serial.print('a', BIN); //vrátí: 1100001 
Serial.print('a', OCT); //vrátí: 141
Serial.print('a', HEX); //vrátí: 61

 


Čtení dat - Serial.available() a Serial.read()

Odesílání dat z Arduina již máme za sebou. Nyní se podíváme na možnosti čtení informací, které do Arduina posílá PC, nebo jiné zařízení. Nejdříve si musíme říci o tom, jak vlastně posílání dat vypadá na nejnižší úrovni. Když do Arduina přijdou informace po sériové lince, nezpracovávají se hned, ale jsou uchovány v "zásobníku" (anglicky buffer). Ten dokáže uchovat až 64 bytů. Čtení poté probíhá tak, že se vezme první byte z bufferu, zpracuje se procesorem, jeho místo se uvolní a uchované byty se posunou dopředu. Poté se vezme další byte, zpracuje se atd. Serial-buffer Když chceme Arduinu odeslat nějakou hodnotu, nejjednodušším způsobem je napsat ji do textového pole v horní části Serial monitoru. Jelikož se odesílá ASCII hodnota znaků, má každý znak (i čísla) vlastní byte paměti.

Funkce Serial.available() a Serial.read() jsem dal dohromady, protože spolu souvisí a využívají se obě najednou. Jako první přichází na řadu funkce Serial.available(). Ta vrátí počet bytů, dostupných v bufferu. Poté dojde na funkci Serial.read(), která vezme první byte z bufferu a přečte ho. Zároveň dojde k vyjmutí bytu z bufferu, což se projeví snížením hodnoty Serial.available() při dalším čtení. Při spuštění následujícího skriptu a odeslání řetězce "AHOJ" z PC do Arduina budou v bufferu obsazené 4 byty (za každý znak jeden). Všimněme si, že při vícenásobném odeslání řetězce se počet bytů v bufferu zvětšuje, protože stále nedošlo ke zpracování předchozích dat.

void setup(){
  Serial.begin(9600);
  Serial.println("Komunikace zahajena");
}

void loop(){
  Serial.print("V bufferu je nyni: ");
  Serial.print(Serial.available());
  Serial.println(" bytu.");
  delay(1000);
}

A nyní si ukažme použití funkce Serial.read(). V tomto příkladu čte Arduino byte po bytu buffer a vypisuje přijaté hodnoty zpět po sériové lince.

char prijato;

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {
    prijato = Serial.read();           
    Serial.print("Prijato: ");
    Serial.println(prijato);
  }
}

 


Ukončení komunikace - Serial.end()

Tato funkce se v praxi moc často nepoužívá. Při jejím volání ale dojde k ukončení sériové komunikace. Nepotřebuje žádný parametr.

Serial.end();

Serial1.end(); //u větších desek s více sériovými linkami

Poznámka: Existuje ještě řada dalších funkcí, které pracují se sériovou linkou. My jsme si představili ty nejzákladnější. Popis zbylých funkcí naleznete v jejich dokumentaci.

 


Cykly

Jistě si vzpomenete, že jsme se v minulých dílech dostali do situace, kdy jsme museli psát prakticky stejnou věc stále dokola, jen s menší obměnou (většinou to byla změna čísla). Pokud se při programování stane, že se nám něco pořád opakuje, přicházejí na řadu cykly. Existují tři druhy cyklů. Ty si teď popíšeme a vysvětlíme si rozdíly mezi nimi. Než se ale pustíme do vysvětlování, ukážeme si složené operátory, které se v cyklech (a nejen tam) často používají.


Složené operátory

Anglicky nazývané compound operators jsou operátory, které nám usnadní práci. Zkracují totiž zápis operací, kdy upravujeme hodnotu pouze jedné proměnné. Popišme si je na příkladech.

//sčítání
x = x + 2; //zvětšili jsme hodnotu x o 2
x += 2; //dosáhneme stejného výsledku s kratším zápisem

//odčítání
x = x - y; //od x odečteme hodnotu y a výsledek zapíšeme do x
x -= y; //výsledek je stejný

//na stejném principu funguje i násobení a dělení
x *= 20;
x /= 30;

Speciálním druhem těchto operátorů jsou x++ a x--. Ty použijeme tehdy, když chceme hodnotu proměnné snížit, nebo zvýšit pouze o 1.

x = 10;
x++; //x má nyní hodnotu 11
x--;
x--; //teď už má hodnotu 9

 


Cyklus while()

Všechny příkazy v cyklu se provádí, dokud je podmínka pravdivá. Za zmínku stojí, že program kontroluje platnost podmínky vždy na začátku cyklu, pokud je nepravdivá, cyklus skončí. Vyjádřeno slovy: "Pokud je podmínka pravdivá, udělej tohle a vrať se na začátek. Pokud není, skonči". Cyklus while() se tedy nemusí provést vůbec. Používá se následující syntaxe:

while(podmínka){
	příkazy...
}

Příklad 1. - ukázka cyklu

boolean x = true;

void setup() {
  Serial.begin(9600);
  while(x){
    Serial.println("OPAKUJI");
    x = false; //cyklus se tedy provede jednou
  } 
}

void loop() {
  
}

Příklad 2. - výpis všech čísel od 0 do 99

byte x = 0;

void setup() {
  Serial.begin(9600);
  while(x < 100){
    Serial.println(x);
    x++;
  } 
}

void loop() {
  
}

 


Cyklus do...while()

Cyklus do..while() se od while() liší pouze v tom, že se podmínky kontrolují až na konci. Dříve tedy dojde k provedení příkladů a poté až ke kontrole podmínek. Slovně: "Udělej něco, a když platí podmínky, vrať se na začátek. Jinak skonči." V praxi to tedy znamená, že se tento cyklus provede minimálně jednou. Syntaxe je následující:
 

do{
    příkazy...
}while (podmínky); //<--všimněme si, že je zde na konci cyklu středník

Příklad 1. - ukázka cyklu

boolean x = true;

void setup() {
  Serial.begin(9600);
  do{
    Serial.println("OPAKUJI");
    x = false; //cyklus se také provede pouze jednou
  } while(x);
}

void loop() {
  
}

Příklad 2. - počítání od 0 do 99

byte x = 0;
 
void setup() {
  Serial.begin(9600);
  do{
    Serial.println(x);
    x++;
  }while(x < 100);
}
 
void loop() {
  
}

 


Cyklus for()

Tento cyklus se používá asi nejčastěji. Jedná se většinou o případy, kdy známe počet opakování cyklu. Také se od ostatních cyklů liší tím, že potřebuje mít pro svoji funkci vlastní proměnnou, která hlídá počet opakování.

for(inicializace proměnné a nastavení hodnoty; podmínka; operace prováděné při každém opakování){
	příkazy...
}

Příklad 1. - výpis čísel od 0 do 99

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

 


Příklad

V tomto díle si vytvoříme hada z pěti LED diod (počet je však volitelný) a ukážeme si, jak se dá cyklus for využít v praxi. Budeme potřebovat:

  1. Desku Arduino
  2. PC
  3. Nepájivé kontaktní pole s vodiči
  4. 5x 330 ohm resistor
  5. 5x LED diodu
byte led[] = {2,3,4,5,6}; //piny s LED diodami
byte pocet = 5; //počet diod
int rychlost = 1000; //jakou prodlevu mají jednotlivá; bliknutí

void setup() {
  for(int i = 0; i < pocet; i++){
    pinMode(led[i], OUTPUT); //nastavení pinů
  }
}
 
void loop() {
  for(int i = 0; i < pocet; i++){
    digitalWrite(led[i], HIGH);
    delay(rychlost/2);
    digitalWrite(led[i], LOW);
    delay(rychlost/2);
  }
}

 


V případě jakýchkoliv dotazů či nejasností se na mě neváhejte obrátit v komentářích.




Kam pokračovat?


<--#4 - Pokročilejší struktury jazyka Wiring||#6 - Užitečné funkce -->
Zpět

Diskusní téma: #5 - Sériová komunikace a Cykly

Datum
Vložil
Titulek

Had pomocí millis

Dobrý den,
Je možné využít pro vytvoření hada z LED funkci millis bez použití Delay s použitím funkce
for(int i = 0; i < pocet; i++){
Pokoušel jsem se o to ale vůbec se mi nedaří.
Děkuji

Datum
Vložil
Titulek

Re: Had pomocí millis

Tak už je vše vyřešeno. musel jsem přidat pomocnou hodnotu stavu ledek

Datum
Vložil
Titulek

sizeof

Jenom poznámka k proměnné pocet, u které není potřeba nastavovat hodnotu ručně, ale lze použít funkci sizeof, která vrátí velikost pole: byte pocet = sizeof(led);

Datum
Vložil
Titulek

tqjcbcsmhv@gmail.com

Datum
Vložil
Titulek

arduino s rele

Dobrý den,
potřeboval bych udělat zdrojový kod pomocí cyklu while. Jde o automatické zavlažování, kde je čidlo půdní vlhkosti, snímač vodní hladiny a relé, které ovládá čerpadlo. V cyklu by mělo být nastaveno, že pokud je v nádrži vody a zároveň je vlhkost půdy nižší než 40%, tak relé sepna a čerpadlo začne zavlažovat do doby dokud nebude prázdná nádrž a nebo vlhkost půdy větší než 60%. Dokáže mi někdo pomoct tímto kodem.

Děkuji

Datum
Vložil
Titulek

Re: arduino s rele

Dobrý den. Nevím, jestli to není pomocí while zbytečně komplikované. Spouštění čerpadla může být přímo v bloku loop().

Mějme dvě proměnné:
byte vlhkost - může mít hodnoty 0 až 100 (podle aktuální vlhkosti)
boolean voda - hodnoty true a false podle toho, jestli je v nádrži voda nebo není

Do proměnné vlhkost budeme vkládat hodnotu aktuální vlhkosti půdy. Postup získání informace o vlhkosti závisí na typu použitého čidla. Často je ale dostupný ukázkový kód pro použití čidla s Arduinem.

Dále musíme zjistit, jestli je v nádrži voda. Nevím jako to máte technologicky vymyšlené. Předpokládejme že je měření založené na principu spínače, kdy voda uzavírá obvod mezi dvěma vodiči. Poté bude nastavení proměnné voda vypadat třeba takto:

voda = digitalRead(pin);

A rozhodování o tom, jestli se relé sepne nebo ne by mohlo být následující:

if((voda && vlhkost < 40) && !(vlhkost > 60)){
digitalWrite(rele, HIGH);
}
else{
digitalWrite(rele, LOW);
}

Snad jsem pomohl. Pokud byste měl ještě nějaké dotazy, napište mi na mail zbysekvoda@seznam.cz.

Datum
Vložil
Titulek

respekt

Zatím jen pročítam a nezkoušel jsem v praxi ale musím říci, že tento serial/návod je opravdu super.

Datum
Vložil
Titulek

chybka se vloudila

Dobrý den
v článku jsem našel chybku
x = x - y; //od x odečteme hodnotu y a výsledek zapíšeme do x
x -= x; //výsledek je stejný
-------------------------------------
x -= y; //místo x -= x

Datum
Vložil
Titulek

Re: chybka se vloudila

Děkuji za upozornění. Opraveno.

© 2015 Všechna práva vyhrazena.

www.hwkitchen.com