Kurz Arduino – #3 – UART (komunikace s PC), proměnné

Arduino umožňuje použití mnoha komunikačních rozhraní. Díky nim mohou vytvořené návrhy komunikovat s jinými obvody, senzory nebo počítačem.

V této části kurzu se budeme zabývat rozhraním UART. Jedná se o jednoduché a velmi oblíbené sériové rozhraní. Zejména při komunikaci s počítačem.

Objednejte si sadu prvků a začněte se učit v praxi! Kliknutím sem přejdete do obchodu >>

Jak funguje UART? - Trocha teorie

Jeho funkční princip je založen na sériovém přenosu posloupnosti bitů, které jsou následně spojeny do informace. Jeden rámec (zjednodušeně řečeno: bajt) je přenášen v následujícím tvaru:

Příklad rámce rozhraní UART

Přenos začíná start bitem, který je na obrázku označen BS. Tento bit je vždy logická nula. V závislosti na konfiguraci následuje 7, 8 nebo 9 datových bitů (zde označených jako B0-B7), které představují přenášené informace. Stop bit (zde označený jako BK bit) je bit, který je logickou jedničkou – označuje konec přenosu.

Naštěstí se nemusíme starat o rozdělování dat na bity/byty/snímky. Všechny operace za nás provádí Arduino.

Při použití UART na Arduinu nás zajímají dva piny:

  • Tx pro odesílání dat (pin 1 na Arduino),
  • Rx pro příjem dat (pin 0 na Arduino).

Aby přenos probíhal správně, musí být na obou čipech nastavena stejná přenosová rychlost – tzv. baud rate. Určuje počet bitů přenesených za sekundu. Obvyklé hodnoty jsou: 9600 a 115200.

Počítač, se kterým chceme komunikovat, musí být také vybaven vhodným rozhraním. Výrobci počítačů bohužel zrušili standardní sériové rozhraní RS-232, kterým byla ještě před několika lety vybavena většina počítačů.

Zbývá komunikace přes USB. Tento úkol je bohužel poměrně obtížný. Z tohoto důvodu většina hobbyistů používá převodníky UART <-> USB, které práci značně usnadňují. S Arduino si s tím nemusíte dělat starosti. Takový převodník je již zabudován v naší desce.
Připojení přes USB.
Proto nemusíme nikde propojovat piny 0 a 1. Jediné, co musíte udělat, je připojit počítač k Arduino pomocí kabelu USB (stejného, který se používá pro programování).

Nyní se dostáváme k praktickým příkladům. První dva programy vyžadují POUZE připojení Arduino k počítači přes USB. Teprve později přidáme do našeho obvodu periferní zařízení.
Hotové sady pro kurzy Forbot
Sada prvků   Zaručená podpora   Odeslání do 24 hodin

U našich prodejců si nyní můžete zakoupit sadu více než 70 elementů potřebných pro cvičení v kurzu!

Populární sada: Arduino MasterRobotics Master

První program Arduino

Úkolem následujícího programu je jednoduše pravidelně odesílat text do počítače:

				
					void setup(){
  Serial.begin(9600); //Nastavení přenosové rychlosti
  Serial.println("Vítejte na Forbot!"); //Jednorázové odeslání textu
}
void loop() {
  delay(5000);
  Serial.println("Uplynulo 5 sekund!"); //Odesílání ve smyčce
}
				
			
Po nahrání výše uvedeného programu se zřejmě nic nestane. Abychom mohli sledovat provoz, musíme v nabídce Arduino vybrat následující položky:>Serial Port Monitor. Otevře se nové okno. Toto okno se obecně označuje jako terminál. Zde můžeme sledovat, co se posílá z/do Arduino přes port COM, tj. náš UART.

Aktualizace, leden 2016 – Od aktualizace prostředí Arduino IDE se funkce označovaná zde jako Serial Port Monitor nachází pod volbou Serial Monitor. Pokud se v programu zobrazují obě možnosti, vyberte nyní možnost Serial Monitor, druhá možnost bude popsána v části 10.

Je důležité, aby terminál pracoval správnou rychlostí na portu COM, ke kterému je Arduino připojeno! Nastavení rychlosti najdete v pravém dolním rohu terminálu.

Průběh programu v praxi:

Text odeslaný do terminálu.
Přejděme nyní k analýze programu. Nejprve je třeba nastavit přenosovou rychlost. K tomu slouží funkce Serial.begin(speed), kde speed určuje přenosovou rychlost. V tomto případě je to 9600 baud/s. Pro odeslání řetězce znaků se však používá funkce – Serial.println(val), kde val je řetězec znaků nebo číslo.

Text „Vítejte ve Forbotu!“ se zobrazí pouze jednou, protože jsme ho vložili do funkce nastavení, a jak je vidět z obrázku předchozí části kurzu – pokyny se provedou až po spuštění programu.

Lze také pozorovat funkční přenos lze také pozorovat na LED diodách zabudovaných v Arduino (označené Tx a Rx)! Rozsvítí se, když probíhá přenos dat z/do naší desky.

Domácí úkol 2.1

Zjistěte, co se stane, když v okně terminálu zvolíte jiné rychlosti, než jaké jsou nastaveny v Arduino. Kdy dochází k chybám? Jak se projevují? Jak sami uvidíte, chyby jsou zcela odlišné a je třeba je brát v úvahu.

Interakce s programem

Informace samozřejmě nemusí být prostřednictvím UART odesílány nepřetržitě; odesílání a přijímání může probíhat i v čase, který si sami zvolíme. To je velmi užitečné, třeba jen pro diagnostiku fungování obvodu nebo signalizaci různých událostí.

Představme si situaci, ve které Arduino hraje roli kontrolky okna, se speciálním tlačítkem/senzorem připojeným k jeho rámu. Celý mechanismus funguje tak, že při zavřeném okně je tlačítko zkratováno na zem, jinak je obvod přerušen.

S dříve získanými znalostmi byste mohli napsat program, který při otevření okna rozsvítí barevnou LED diodu. Pokusme se však náš program rozšířit. Arduino vybavíme tlačítkem (které imituje senzor v okenním rámu) a dvěma LED diodami (zelenou a červenou).

Když je okno zavřené (stisknuté tlačítko), rozsvítí se zelená LED dioda. Pokud přerušíte obvod (přestanete mačkat tlačítko), musí se rozsvítit červená LED dioda a na terminálu se zobrazí „Pozor! Alarm! Okno není zamčené!

				
					void setup(){
  Serial.begin(9600); //Spouštíme vysílání
  
  pinMode(8, OUTPUT); //Výstup červené diody
  pinMode(9, OUTPUT); //Výstup zelené diody
  pinMode(10, INPUT_PULLUP); //Tlačítko
  
  digitalWrite(8, LOW); //Vypnutí obou diod LED
  digitalWrite(9, LOW);
}

void loop() {
  if (digitalRead(10) == LOW) { //Pokud je tlačítko stisknuto
    digitalWrite(9, HIGH); //Zapnutí zelené diody 
    digitalWrite(8, LOW); //Vypnutí červené diody
  } else { //Pokud tlačítko není stisknuto
    digitalWrite(9, LOW); //Vypnutí zelené diody
    digitalWrite(8, HIGH); //Zapnutí červené diody
    Serial.println("Pozor! Alarm! Okno není zamčené!");
  }
}
				
			

Podívejme se, jak program funguje! Bohužel není ideální, pokud okno není zavřené, protože informace o alarmu se odesílají trvale. Byli bychom však raději, kdyby byla odeslána pouze jednou. Máte nějaký nápad, jak to změnit? Samozřejmě pomocí smyčky while, kterou jsme používali dříve.

				
					void setup(){
  Serial.begin(9600); //Spouštíme vysílání
  
  pinMode(8, OUTPUT); //Výstup červené diody
  pinMode(9, OUTPUT); //Výstup zelené diody 
  pinMode(10, INPUT_PULLUP); //Tlačítko
  
  digitalWrite(8, LOW); //Vypnutí obou diod 
  digitalWrite(9, LOW);
}

void loop() {
  
  if (digitalRead(10) == LOW) { //Pokud je tlačítko stisknuto
    digitalWrite(9, HIGH); //Zapnutí zelené diody 
    digitalWrite(8, LOW); //Vypnutí červené diody
  } else { //Pokud tlačítko není stisknuto
    digitalWrite(9, LOW); //Vypnutí zelené diody
    digitalWrite(8, HIGH); //Zapnutí červené diody
    Serial.println("Pozor! Alarm! Okno není zamčené!");
    
    while (digitalRead(10) == HIGH) {
      //Zastavíme se v prázdné smyčce, dokud se okno opět nezavře.
      delay(25); //Ve smyčce zavádíme malé zpoždění 25 ms, abychom kompenzovali rušení.
    }
 
  }
}
				
			

Můžeme výše uvedený kód nějak vylepšit? Zlepšit ne, ale samozřejmě ji můžeme udělat elegantněji.

Pokyn #define

Postupem času se naše programy výrazně rozšiřují. Ale co když potřebujeme změnit fyzické zapojení například diody nebo tlačítka? Bylo by nepohodlné měnit číslo PIN všude v programu, že?

Na pomoc nám přichází direktiva #define. Umožňuje definovat symbol, který se před kompilací nahradí všude v programu. Například:
				
					#define ledPin 8

void setup() {
  pinMode(ledPin, OUTPUT); //Konfigurace pinu 8 jako výstupu
}
 
void loop() {
  digitalWrite(ledPin, HIGH); //Zapnutí diody
  delay(1000); //Čekání 1 sekundu
  digitalWrite(ledPin, LOW); //Vypnutí diody
  delay(1000); //Čekání jednu sekundu
}
				
			

Vložením řádku na začátek programu: #define ledPin 8, zajistíme, že každý výskyt textu ledPin bude před kompilací převeden na 8. Převedený název může být samozřejmě jiný, důležité je, aby byl jedinečný a pomohl vám při psaní dlouhých programů.

Nezapomeňte!

Za řádky začínající #define nepište středník!

Níže naleznete konečnou, revidovanou verzi našeho okenního alarmu:

				
					#define diodaČervená 8
#define diodaZelená 9
#define senzorOkna 10

void setup(){
  Serial.begin(9600); //Spouštíme vysílání
  
  pinMode(diodaČervená, OUTPUT); //Výstup červené diody
  pinMode(diodaZelená, OUTPUT); //Výstup zelené diody
  pinMode(senzorOkna, INPUT_PULLUP); //Tlačítko
  
  digitalWrite(diodaČervená, LOW); //Vypnutí obou diod 
  digitalWrite(diodaZelená, LOW);
}

void loop() {
  
  if (digitalRead(senzorOkna) == LOW) { //Pokud je tlačítko stisknuto
    digitalWrite(diodaZelená, HIGH); //Zapnutí zelené diody
    digitalWrite(diodaČervená, LOW); //Vypnutí červené diody
  } else { //Pokud tlačítko není stisknuto
    digitalWrite(diodaZelená, LOW); //Vypnutí zelené diody
    digitalWrite(diodaČervená, HIGH); //Zapnutí červené diody
    Serial.println("Pozor! Alarm! Okno není zamčené!");
    
    while (digitalRead(senzorOkna) == HIGH) {
      //Zastavíme se v prázdné smyčce, dokud se okno opět nezavře.
      delay(25); //Ve smyčce zavádíme malé zpoždění 25 ms, abychom kompenzovali rušení.
    }
 
  }
}
				
			

Od nynějška bude změna vývodu, ke kterému připojíme diodu nebo senzor, vyžadovat pouze změnu v jednom bodě programu.

Proměnné nebo "univerzální zásuvky"

Než přejdeme k dalšímu programování (včetně odesílání informací do Arduina přes UART), musíme se vypořádat s proměnnými. Jak je definováno v názvu této části, proměnné jsou univerzální zásuvky, do kterých můžeme vkládat určité hodnoty.

Přesněji řečeno, mohou to být znaky, slova nebo čísla. Nejčastěji budeme pracovat s číselnými proměnnými. Kdy jsou proměnné nutné? Pokud chceme uložit hodnotu a provádět s ní různé operace. Stejně jako funkce může mít i proměnná specifický typ, který určuje, jaká data může uchovávat.

Zde je seznam nejdůležitějších typů dat:

Maximální hodnoty, které můžeme do proměnné zapsat, závisí na obvodu, který programujeme. Výše uvedené hodnoty jsou uvedeny pro Arduino UNO.


Zvláštní pozornost věnujte proměnným typu long a float.

				
					boolean logika = false; //Boolean - hodnota pravda (true) nebo falsch (false)

int číslo = 30000; //Int - celková čísla v rozsahu -32,768 až 32,767 (v Arduino Uno)
long velkeČíslo = 2000000; //Long - celková čísla v rozsahu -2 147 483 648 až 2 147 483 647

float čísloProměnné = 6.28; //Float - reálná čísla zabírající v paměti 4 bajty.

char znak = 'a'; //Char - ukládá jeden znak
String věta = "Vítejte na Forbot!"; //String - řádek znaků
				
			
Na začátku budete používat především proměnné následujícího typu:
  • boolean
  • int
  • string
Boolean se používá, jak již bylo zmíněno, k uložení hodnoty true nebo false. Tento typ se nejčastěji používá k signalizaci událostí nebo kontrolních podmínek.

Proměnná Int je nejčastěji používanou proměnnou pro ukládání celých čísel. Může sloužit k ukládání informací, například kolikrát bylo stisknuto tlačítko, kolikrát byla spuštěna událost nebo údaje ze snímače vzdálenosti (tomu se budeme věnovat na konci kurzu). S proměnnými lze samozřejmě provádět i matematické operace – více v praktických příkladech.

String, je řetězec, takže zjednodušeně řečeno, do proměnných tohoto typu můžeme ukládat text. Slova, věty, zprávy.

Proměnné v praxi

Chceme-li proměnnou použít, musíme ji deklarovat, tj. sdělit překladači její typ a název. Naštěstí se nemusíme starat o alokaci speciálního paměťového místa v mikrokontroléru.

Název každé proměnné začíná písmenem. Číslice nesmí být na začátku. Každé prohlášení musí být učiněno následujícím způsobem:
				
					Typ název = 0;
//nebo
Typ název;
				
			

Všimněte si, že operátor = se používá k přiřazení hodnoty proměnné a že operátor == slouží ke kontrole, zda se výraz (proměnná) rovná hodnotě.

Obě výše uvedené metody by měly poskytnout totožný výsledek. První metoda je však bezpečnější, protože přiřadí proměnné počáteční hodnotu ihned po jejím vytvoření. Druhá metoda by teoreticky měla také generovat proměnnou s počáteční hodnotou nula. To platí v 99,99 % případů. Je však dobrým zvykem nastavit počáteční hodnoty při deklaraci ručně.

Proměnné mají vlastnost zvanou scope. Pokud je proměnná umístěna ve funkci, proceduře nebo podprogramu, není v jiném podprogramu viditelná (nemůžeme ji použít). Několik příkladů:
				
					int Proměnna = 0; //Globální proměnná - viditelná všude v programu

void setup() {
  int Proměnna2 = 0; //Lokální proměnná - viditelná pouze ve funkci setup()
}

void loop() {
  int Proměnna3 = 0; //Lokální proměnná - viditelná pouze ve funkci loop()
}
				
			
Možná vám to ještě není úplně jasné, ale buďte si jisti – vše se časem naučíte v praxi. Pro začátek však budeme pro zjednodušení používat pouze globální proměnné. Zkušení programátoři vás možná upozorní, že to není správně – ale my se k tomu budeme přibližovat pomalu.

Je tu ještě jedna velmi důležitá věc – pojmenování proměnných. Nezapomeňte dát proměnným názvy, které označují jejich účel. Takže například místo od:
				
					string xx = "Damian"; //Proměnná ukládající jméno
				
			

měla by být vytvořena proměnná:

				
					string jméno = "Damian"; //Proměnná ukládající jméno
				
			

Problém může nastat, pokud je účel proměnné složitější. Nebojte se však názvů, jako jsou např.:

				
					int rychlostLevehoMotoru = 100; //Rychlost levého motoru
				
			

Nejdůležitější je, aby byl program čitelný.

Názvy proměnných samozřejmě nesmí obsahovat mezery!

Použití proměnných - čítače

Teorii máme za sebou – nyní je čas na praxi. Začněme něčím velmi jednoduchým. Nejprve necháme náš program v každé smyčce vypsat hodnotu proměnné, kterou chceme zvýšit.

				
					int čítač = 0; //Deklarace proměnné

void setup() {
  Serial.begin(9600); //Inicializace připojení PC
}

void loop() {
  Serial.println(čítač); //Odeslání hodnoty proměnné čítač
  čítač = čítač + 1; //Zvýšení čítače o 1
  delay(100); //Zpoždění pro zviditelnění efektu
}
				
			

Deklarace globální proměnné je samozřejmě na samém začátku. To znamená, že k nim můžeme přistupovat odkudkoli z programu. Následně se převodovka spustí jako obvykle. Naše hlavní smyčka loop() provede 3 akce.

  1. Přistupujeme k paměťovému místu, které jsme deklarovali jako proměnnou čítače, a poté odešleme hodnotu, kterou jsme tam našli, prostřednictvím UART.
  2. Aktuální hodnotu proměnné čítače zvýšíme o 1.
  3. Počkáme 100 ms (pro lepší efekt) a vrátíme se na začátek smyčky.

Bod 2, tj. zvýšení počtu proměnných, může vyžadovat vysvětlení. Používá se následující zápis:

				
					čítač + čítač + 1; //Zvýšení čítače o 1
				
			

Z matematického hlediska, kde znaménko „=“ znamená rovnost, by výše uvedený řádek neměl fungovat. V programování však znaménko „=“ znamená přiřazení. V praxi by se výše uvedený kód měl chápat jako následující operace:

  1. Převezměte hodnotu proměnné čítače,
  2. přidat 1,
  3. uložit výslednou hodnotu do proměnné čítače.

Nahrajte program do Arduina a zkontrolujte, zda funguje správně. Abyste viděli účinky, musíte samozřejmě spustit monitor na sériovém rozhraní (terminálu).

Das Ergebnis des ersten Programms mit einer Variablen.

Domácí úkol 2.2

Co se stane, když deklaraci proměnné čítače přesuneme do smyčky loop()?

Domácí úkol 2.3

Jak již bylo zmíněno, proměnné mají svá omezení. Změňte deklaraci proměnné čítače z int na byte, která může obsahovat čísla v rozsahu 0-255. Co se stane, pokud je tato hodnota překročena? Pozorně sledujte zobrazené výsledky!

Obousměrný přenos pomocí Arduino

Aby byla komunikace užitečná, měla by být samozřejmě obousměrná. Arduino nám zatím posílá informace. Nyní je čas, abychom mu „odpověděli“.

Úkolem prvního programu bude „naslouchat“ našemu jménu. Po odeslání by nás Arduino mělo přivítat zprávou „Hello name!“, přičemž se samozřejmě vloží dříve odeslané jméno.

Nejprve deklarujeme proměnnou přijatáData, do které se zkopíruje řetězec přijatých znaků. Seznámíme se s novou funkcí: Serial.available(). Tato funkce vrací počet bajtů, které byly přijaty a čekají na zpracování přes Arduino.

Pokud jsou data k dispozici (více než 0), zapíší se do proměnné přijatáData. K tomu slouží funkce Serial.readStringUntil(terminator), která kopíruje data z vyrovnávací paměti, dokud nenastane ukončovací znak (v tomto případě „\n“ – tj. přerušení řádku).

				
					String prijataData = ""; //Prázdný řádek přijatých dat

void setup() {
  Serial.begin(9600); //Zahájení komunikace
}

void loop() {
  if(Serial.available() > 0) { //Přijalo Arduino data
    prijataData = Serial.readStringUntil('\n'); //Pokud ano, přečtěte je až po znak konce řádku a uložte je do proměnné receivedData.
    Serial.println("Ahoj" + prijataData + "!"); //Zobrazte zprávu

  }
}
				
			

Interakce s obvodem - ovládání diod přes UART

K ovládání LED diod využijeme možnost posílání textu do Arduino. Za tímto účelem připojte dvě diody podle obrázku níže (diody na pinech 8 a 9).

Úkolem našeho programu je rozsvítit zelenou nebo červenou LED diodu na 1 sekundu, když je do Arduino odeslán příslušný příkaz. Hotový program vypadá takto:

				
					#define zelená 8
#define červená 9

String prijataData = ""; //Prázdný řádek přijatých dat

void setup() {
  Serial.begin(9600); //Zahájení komunikace
  pinMode(zelená, OUTPUT); //Konfigurace výstupu
  pinMode(červená, OUTPUT);
  
  digitalWrite(zelená, LOW); //Vypnutí diod 
  digitalWrite(červená, LOW);
}

void loop() {
  if(Serial.available() > 0) { //Přijato Arduino data
    //Pokud ano, přečtěte je až po znak konce řádku a uložte je do proměnné prijataData
    prijataData = Serial.readStringUntil('\n'); 
    
    if (prijataData == "zelená") { //Pokud příjato slovo "zelená"
      digitalWrite(zelená, HIGH); //To zapne zelenou diodu 
      delay(1000);
      digitalWrite(zelená, LOW); 
    }
    
    if (prijataData == "červená") { //Pokud příjato slovo "červená"
      digitalWrite(červená, HIGH); // To zapne červenou diodu
      delay(1000);
      digitalWrite(červená, LOW); 
    }
  }
}
				
			

Nyní si rozebereme, jak program funguje. Na začátku jsou definována čísla pinů LED diod a je deklarována proměnná, do které se kopírují přijatá data. Poté ve smyčce zkontrolujeme, zda Arduino data přijalo. Pokud ano, zkontrolujeme, zda tento údaj odpovídá jedné z barev. Následně se zapnou příslušné diody.

Aby výše uvedený program fungoval, musí být odeslány znaky konce řádku, které lze automaticky připojit k zadanému řetězci znaků. Za tímto účelem vyberte příslušnou možnost z rozevírací nabídky v monitoru sériového rozhraní.

Domácí úkol 2.4

Přepracujte výše uvedený program tak, aby v případě nesprávné barvy odeslal prostřednictvím terminálu odpovídající zprávu.

Domácí úkol 2.5*

Obtížnější úkol s hvězdičkou. Napište program, který v okamžiku, kdy je diodě vyslána barva, změní její stav na opačný. Když je dioda zapnutá, je vypnutá a naopak. Tip: Nezapomeňte použít další proměnné typu bool pro uložení aktuálního stavu LED diod.

Shrnutí

Původně měla být tato část kurzu delší. Záměrně jsem však některé materiály zkrátil, abych nepředkládal příliš mnoho nového materiálu najednou. Tato „extrahovaná část“ materiálu se objeví v další části kurzu.Po přečtení této části by měl být každý schopen napsat program, který jednoduchým způsobem komunikuje s počítačem. Lekce byla poměrně snadná, ale měli byste si na ni vyhradit dostatek času.

Nezapomeňte, že kompletní sada potřebná pro všechna cvičení je k dispozici u společnosti Botland. Zakoupením souprav podpoříte budoucí vydání kurzu Forbot!

Proměnné je programovací prvek, který se bude objevovat stále a opakovaně! UART budeme také často používat k zobrazení shromážděných informací a ke změně nastavení našeho programu. Bude také velmi užitečný při hledání chyb v našich kódech!

Objednejte si sadu prvků a začněte se učit v praxi! Kliknutím sem přejdete do obchodu >>

Přejít nahoru