Kurz Arduino – #6 – Pokračování UART, serva

V této části se budeme zabývat několika tématy. Nejprve se na chvíli vrátíme k UART, probereme také nové řídicí instrukce. A nakonec praktické využití serva.

Před čtením se ujistěte, že znáte základy popsané v předchozích částech našeho bezplatného kurzu Arduino!

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

Zobrazení čísel v terminálu

Ve třetí části kurzu jsme ke komunikaci s počítačem použili rozhraní UART. Tam jsme používali funkce pro odesílání a přijímání informací. Tehdy jsem však neprobíral všechny možnosti, které zdánlivě jednoduchá funkce println nabízí. V dosavadních příkladech byl použit ve své nejjednodušší podobě, tj. s jediným argumentem – číslem nebo znakovým řetězcem, který se má odeslat.

Informace viditelné na terminálu jsou čitelné pro člověka, protože Arduino je převádí na kódy ASCII. Sudá čísla se odesílají jako text.

Všechny odeslané hodnoty v úlohách, které jsme provedli, jsme zobrazili v desetinném tvaru. Co kdybychom například chtěli reprezentovat čísla ve formátu binárním? Musíme vytvářet vlastní funkce pro převod reprezentace hodnot do jiných systémů? Ne!

Na tomto místě je vhodné připomenout, že počítače používají především binární soustavu, takže jakákoli jiná soustava (včetně naší, desítkové) je uměle vynucená.

Knihovny Arduino naštěstí obsahují řadu nástrojů pro koncové uživatele. To zahrnuje podporu různých systémů zobrazování čísel. Pokud odešleme hodnotu pomocí println, můžeme rozhodnout, jak se má zobrazit na počítači:

				
					[...]
int číslo = 2345;

Serial.println(číslo);       //Zobrazení v desítkové soustavě
Serial.println(číslo, DEC);  //Zobrazení v desítkové soustavě

Serial.println(číslo, HEX);  //Zobrazení v hexadecimální soustavě
Serial.println(číslo, OCT);  //Zobrazení v oktalové soustavě
Serial.println(číslo, BIN);  //Zobrazení v binární soustavě
[...]
				
			

Jak ukázaly pokusy v předchozích částech, čísla se ve výchozím nastavení zobrazují v desítkové soustavě, ale můžete si také vybrat mezi hexadecimální, oktalovou a binární reprezentací. V praxi se bude pravděpodobně nejčastěji používat standardní desítková reprezentace a binární reprezentace.

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

Přesnost čísel s pohyblivou řádovou čárkou

Jak již bylo řečeno v předchozích částech kurzu, je také možné deklarovat proměnnou pro uložení čísla s pohyblivou řádovou čárkou, např. 3.141592: 3.141592 Takovými příklady jsme se zatím nezabývali, protože se ukázalo, že je dobré se na mikrokontrolérech vyhýbat číslům s desetinnými místy.

Takové operace jsou pro počítače poměrně náročné a vyžadují více času.

Předpokládejme však, že takové číslo chceme bezpodmínečně zobrazit. K tomu lze použít následující velmi krátký program:

				
					void setup() { 
  float čísloPI = 3.1415; //Deklarace proměnné

  Serial.begin(9600); //Inicializace UART
  Serial.println(čísloPI, 4); //4 místa za desetinnou čárkou
  Serial.println(čísloPI, 0); //0 míst za desetinnou čárkou
  
  Serial.println(PI); //Hádanka
} 

void loop() { 
}
				
			

Spuštění výše uvedeného programu by mělo mít následující účinky:

Přesnost zobrazených čísel.

Jak vidíte, při přenosu čísla může být dalším parametrem číslice, která určuje přesnost, s jakou mají být hodnoty zobrazeny. Je důležité si uvědomit, že proměnné typu float se zobrazují až se 7 číslicemi – bez ohledu na to, kolik číslic je za nebo před desetinnou čárkou. Například:

  • float number1 = 0.123456 – správně
  • float number2 = 12345.6 – správně
  • float number3 = 123,456 – správně
  • float number4 = 1234567.8 – false

Pro vyšší přesnost (15 číslic) použijte dvojité proměnné!

Vraťme se krátce k výše zmíněnému programu, zejména k záhadné linii:

				
					Serial.println(PI); //Hádanka
				
			

Proč je výsledkem tohoto příkazu hodnota 3,14? Takovou proměnnou jsme nikde nedeklarovali. Hodnota čísla Pí se používá tak často, že v mnoha jazycích existují hotové konstanty, které se číslu Pí také přibližují. V tomto případě se jedná o PI, které je někde v programu převedeno na odpovídající hodnotu.

Ale POZOR: můžete se zde vystavit velmi vážným rizikům!

Pokud například usilujeme o vysokou přesnost našeho programu, můžeme vyvolat tuto funkci:

				
					Serial.print(PI, 25);
				
			

K naší radosti se pak na displeji zobrazí velmi přesná hodnota:

3.1415927410125732421875

Pokud se však vrátíme zpět do paměti k maximální přesnosti, kterou nabízejí mikrokontroléry, ukáže se, že konstanta PI má vlastnosti proměnné typu float, tj. potřebuje pouze 7 číslic! Více než to je špatně, protože nejpřesnější reprezentace hodnot s plovoucí desetinnou čárkou ve dvojkové soustavě neumožňuje získat odpovídající „pravé Pí“. Více informací najdete v této speciální kalkulačce.

Následující složení je naše zobrazené číslo a skutečné číslo Pí:

3.14159274101257324218750
3.14159265358979323846264

Výše uvedené příklady ukazují, že byste se měli vyhnout číslům s pohyblivou desetinnou čárkou, kdykoli je to možné (v 99 % případů se bez jejich pomoci obejdete). Pokud je přesto používáme, pak se zdravým rozumem a s vědomím jejich omezené přesnosti!

Vše v nové řadě?

Dříve se každá hodnota zobrazená v terminálu zobrazovala na novém řádku. Bylo to čtivé, ale ne vždy užitečné. Ale co když chcete zobrazit několik proměnných a textů vedle sebe? DNápravu zde poskytuje funkce println (bez přípony ln řádku).

Má přesně stejné hodnoty jako dříve používaná funkce println. Jediným rozdílem je, že každá odeslaná hodnota se zobrazí na novém řádku. Příklad:
				
					void setup() { 
  Serial.begin(9600); //Inicializace UART
} 

void loop() { 
  Serial.print("Vítejte v kurzu na Forbot.com! "); //Zobrazení textu
  delay(1000); //Zpoždění pro větší pohodlí
}
				
			

Program neohromí svými schopnostmi, ale předvádí to podstatné („žádný nový řádek“):

Jak mohu přepnout na nový řádek na určité pozici? Třemi způsoby:

				
					Serial.print("První řádek");
Serial.println();
Serial.print("Druhý řádek");

NEBO

Serial.println("První řádek");
Serial.print("Druhý řádek");

NEBO

Serial.print("První řádek \n Druhý řádek");
				
			
Zdaleka nejzajímavějším druhem je ten poslední. Objeví se nový symbol\n. Není pro Arduino jedinečná a označuje přechod na novou řadu. Jak vidíte, je to velmi praktické, protože řadu můžete kdykoli přerušit.

Existují nějaké další užitečné symboly tohoto typu? Ano! Použití tabulátorů (odrážek, širokých mezer) může být stále užitečné pro formátování zobrazeného textu. Chceme-li posunout text doprava, použijeme symbol \t – Tab místo těžkopádného zadávání několika mezer.

Nové informace o UART v praxi

Nyní je čas uvést výše uvedené informace do praxe. Cílem našeho programu je změřit hodnotu napětí na pinu A5 a následně ji zobrazit v terminálu. Tentokrát však nestačí zobrazit číslo v desetinných číslech. Kromě toho by se hodnoty v HEX, OCT a BIN měly zobrazovat na jednom řádku. Samozřejmě by to celé mělo být pěkně naformátované!

Na začátku je třeba zapojit jednoduchý obvod. Ke změně napětí jsem použil potenciometr. Můžete však také instalovat dělič napětí s fotorezistorem. Pokud si nemůžete vzpomenout, jak to udělat, podívejte se na 4. část kurzu.
Potenciometr připojený k A5.

Doufám, že vám předchozí informace nezpůsobily příliš mnoho problémů, a proto bych vám chtěl rovnou ukázat hotový program. Samozřejmě byste měli analyzovat, jak to funguje, a napsat podobný program sami!

				
					void setup() {
  Serial.begin(9600); //Inicializace UART
}

void loop() {
  int potenciometr = analogRead(A5); //Načtení hodnoty ADC
  
  Serial.print("Čtení: ");
  Serial.print(potenciometr, DEC);
  Serial.print("[DEC]\t");
  Serial.print(potenciometr, HEX);
  Serial.print("[HEX]\t");
  Serial.print(potenciometr, OCT); 
  Serial.print("[OCT]\t");
  Serial.print(potenciometr, BIN); 
  Serial.print("[BIN]\n");
  
  delay(1000); //Zpoždění pro pohodlí
}
				
			

Po spuštění programu byste měli v terminálu vidět pěkně naformátované hodnoty ADC v různých číselných reprezentacích:

Rád bych vás povzbudil, abyste sami experimentovali. Je to také ideální příležitost k procvičení ručního převodu čísel do různých soustav. Tím končí část o UART, je čas pokračovat.

Domácí úkol 6.1

Napište program, který přečte informace ze dvou fotorezistorů a potenciometru. Pokud je následně stisknuto tlačítko připojené k zařízení Arduino, je jednou odeslán řádek s informacemi:

„Fotorezistor 1: XXX, fotorezistor: XXX, potenciometr: XXX, tlačítko stisknuto XX krát“.

Místo X se samozřejmě zobrazí správné hodnoty.

Pokyny pro ovládání přepínače

Je čas probrat velmi často používanou instrukci pro ovládání přepínače. Používá se v situacích, kdy na základě proměnné provádíme několik různých akcí v závislosti na kontrolované hodnotě.

Pro pochopení příkazu switch použiji příklad, který je pak řešen dvěma způsoby – tradičně a pomocí nové metody. Předpokládejme tedy, že chceme napsat program, který přečte hodnotu ADC a pak nám ji pošle zpět jako desítkové, šestnáctkové, osmičkové nebo dvojkové číslo. Vše záleží na naší volbě.

S našimi současnými znalostmi bychom mohli napsat program, který používá podmínky:
				
					String prijataData = ""; //Prázdný řetězec přijatých dat

void setup() {
  Serial.begin(9600); //Inicializace UART
}

void loop() {
  int potenciometr = analogRead(A5); //Načtení hodnoty ADC
  
  if(Serial.available() > 0) { //Přijalo Arduino data
    prijataData = Serial.readStringUntil('\n'); //Pokud ano, přečtěte je až po značku konce řádku
  }
  
  if (prijataData == "d") {
    Serial.println(potenciometr, DEC);
  } else if (prijataData == "h") {
    Serial.println(potenciometr, HEX);
  } else if (prijataData == "o") {
    Serial.println(potenciometr, OCT);
  } else if (prijataData == "b") {
    Serial.println(potenciometr, BIN);
  }

  delay(1000); //Zpoždění pro pohodlí
}
				
			

Je to proveditelné? Ano. Pohodlné? Průměrné, zvláště pokud by se objevilo více podmínek nebo by se podmínky musely náhle změnit. Nový pokyn k ovládání spínací skříňky je užitečný. Vypadá to takto:

				
					switch (HodnotaDoKontroly) {
  case Hodnota_1:
    //Kód, který se provede, pokud je splněna podmínka
  break;

  case Hodnota_2:
    //Kód, který se provede, pokud je splněna podmínka
  break;

  [...]

  default: 
    //Kód, který se provede, pokud není splněna žádná podmínka
  break;
}
				
			

Na začátku napíšeme klíčové slovo switch a do kulatých závorek uvedeme proměnnou, kterou chceme kontrolovat. V příkladu podobném předchozímu s if by to bylo takto:

				
					switch (prijataData) {
				
			
Následně otevřeme kroucenou závorku. Do těchto závorek můžeme zapsat libovolný počet podmínek, které se mají kontrolovat jedna po druhé. To provedeme tak, že napíšeme slovo case a za mezeru vložíme hodnotu, kterou musí mít kontrolovaná proměnná. Celá věc končí dvojtečkou „:“.

Pokud je podmínka splněna, provede se kód od podmínky až po další slovo break, které celý přepínač ukončí. Pokud podmínka není splněna, část kódu je ignorována a mikrokontrolér pokračuje v kontrole další podmínky (case).

Pro připomenutí!
Příkaz „switch-case“ je velmi užitečný, pokud chcete zkontrolovat, zda jsou hodnoty stejné!

Nakonec můžeme volitelně umístit kód mezi default a break. Tato funkce se provede, pokud není splněna žádná z předchozích podmínek. Vím, že to zní složitě, a proto nyní přejdeme k praktickému příkladu a zrevidujeme předchozí program.

Malá poznámka: Instrukce pro ovládání přepínačů funguje pouze na základě porovnávání čísel. Proto se v tomto příkladu s písmeny, která kontrolujeme: d, h, o, b, nesmí zacházet jako s písmeny, ale jako s kódy ASCII. Pokud napíšete písmena v jednotlivých apostrofech vedle velkého písmene, zachází se s nimi přesně jako s kódy ASCII.

Kromě toho se data načítají místo dříve používané funkce:
				
					prijataData = Serial.readStringUntil('\n');
				
			

Používá se jednodušší verze funkce, která čte pouze první bajt (znak) dat:

				
					int prijataData = 0; //Prázdný řetězec přijatých dat

void setup() {
  Serial.begin(9600); //Inicializace UART
}

void loop() {
  int potenciometr = analogRead(A5); //Načtení hodnoty ADC
  
  if(Serial.available() > 0) { //Přijalo Arduino data
    prijataData = Serial.read(); //Pokud ano, přečtěte 1 znak
  }
  
  switch (prijataData) {
    case 'd':
      Serial.println(potenciometr, DEC);
    break;
    case 'h':
      Serial.println(potenciometr, HEX);
    break;
    case 'o':
      Serial.println(potenciometr, OCT);
    break;
    case 'b':
      Serial.println(potenciometr, BIN);
    break;
  }

  delay(1000); //Zpoždění pro pohodlí
}
				
			
				
					prijataData = Serial.read(); //Pokud ano, přečtěte 1 znak
				
			

To nám umožnilo porovnat odeslané příkazy a provést odpovídající operace. Doufám, že se vám návod na přepínání stane ještě jasnější, až provedeme další praktické příklady.

Domácí úkol 6.2

Vraťte se k domácímu úkolu 2.4, který byl ve třetí části kurzu Arduino a tentokrát jej proveďte pomocí instrukce switch.

Servomechanismus v praxi - Světelný indikátor

Je čas na slíbené použití serva v praxi. Stále více informací je v současnosti prezentováno digitálně, tedy na displeji.

Některé hodnoty, jako je teplota, intenzita světla atd., se však lépe zobrazují na tradičních analogových displejích. Tedy ty, které mají ukazatel:
Analogové displeje.
Proto se nyní chystáme postavit analogový měřič světla s mikroservem. Ukazatel na jednom rameni ukazuje množství světla dopadajícího na senzor. K tomu potřebujeme Arduino s fotorezistorem zapojeným v obvodu děliče napětí a servo.

Montážní plán hotového spotřebiče je následující:

Je to trochu složitější, ale ve skutečnosti se skládá ze dvou jednoduchých schémat. První je připojení serva spolu s napájením ze stabilizátoru. Kromě toho se přímo vedle tohoto regulátoru napětí objevují dva kondenzátory. Podobné schéma lze nalézt v předchozí části kurzu Arduino.

Při připojování serva a baterie buďte opatrní, abyste nic nepoškodili!

Instalační schéma.

Druhá část schématu spočívá v propojení rezistoru a fotorezistoru do děliče napětí. Podrobnější informace naleznete v části o ADC v Arduino.

Mechanická část projektu

O „profesionálním“ ciferníku a ručičkách se vyplatí přemýšlet hned. Ciferník jsem vyrobil z několika nalepených vizitek a vytištěné stupnice. Ukazatel byl vyroben podobným způsobem. Ke spojení dílů doporučuji použít horké lepidlo (z pistole) nebo oboustrannou lepicí pásku.

Pokud použijete superlepidlo, hrozí nebezpečí, že se pohyblivé části serva slepí k sobě, což je pak vhodné pouze k vyhození!

Analogový ciferník bez ručiček.
Pohled zezadu.

Kvalita konstrukce není nejlepší, ale jsou to jen pokusy a důležitý je efekt:

Hotový analogový displej.

Program je poměrně jednoduchý. Jeho úkolem je cyklicky měřit světlo dopadající na fotorezistor a řídit nastavení serva. K tomu sloužily především již naučené funkce:

				
					#include <Servo.h> //Knihovna odpovědná za servopohony
 
Servo servomechanismus;  //Vytvoříme objekt, abychom se mohli na servo odkazovat. 
byte pozice = 0; //Aktuální poloha serva 0-180
int predchoziPozice = 0;

void setup() { 
  servomechanism.attach(11);  //Servo připojené na pin 11
  Serial.begin(9600);
} 
 
void loop() 
{ 
  int cteniSenzoru = analogRead(A5); //Načtení hodnoty ze snímače
  pozice = map(cteniSenzoru, 0, 900, 0, 180); //Nahradíme ji polohou serva.
  
  if (abs(pozice-predchoziPozice) > 5) { //Zkontrolujeme, zda se polohy liší o více než 5 stupňů.
    servomechanism.write(pozice); //Provedeme krok
    predchoziPozice = pozice; //Pamatujeme si aktuální pozici jako předchozí
  }
  
  Serial.println(cteniSenzoru); //Odesláme hodnotu do terminálu
  delay(300); //Zpoždění pro lepší efekt
}
				
			
Novou funkci abs() je třeba vysvětlit. Je velmi užitečná v situacích, jako je výše uvedená, protože vrací absolutní hodnotu. To znamená, že ať už od většího čísla odečteme menší, nebo naopak, dostaneme kladný výsledek.

V tomto programu také ukládáme aktuální polohu serva do globální proměnné PreviousPosition. Tím je zajištěno, že v dalším cyklu smyčky provedeme pohyb pouze v případě, že dojde k velké změně intenzity světla. Jinak by náš ukazatel mohl oscilovat. Doporučuji experimentovat s hodnotou, ze které pohyb provádíme.

Každý by si měl systém kalibrovat podle vlastních podmínek!
(popis níže)

Program je velmi jednoduchý, takže neobsahuje žádné automatické kalibrační mechanismy. Proto posílá aktuální hodnotu snímanou světelným senzorem do počítače prostřednictvím UART. Nejrychlejší kalibrací by mohlo být převzetí nejnižší a nejvyšší hodnoty pozorované při zakrytém a osvětleném senzoru. Ty by pak měly být zohledněny v tomto řádku:

Analogové zobrazení pomocí Arduino.

Doporučuji změnit nastavení a vyzkoušet nové programy. Doporučuji však nepohybovat servem příliš rychle. To může vést k problémům nebo poškození relativně citlivého motoru. Doporučuji, abyste v tomto řádku nepřekračovali hodnotu 100 ms:

				
					delay(300); //Zpoždění pro lepší efekt
				
			

Domácí úkol 6.3

Zpřesněte obvod pomocí analogového displeje. Zkuste přidat kalibrační mechanismus. Najděte další praktické využití takového obvodu!

Shrnutí

Další, doplňková část se stala poměrně dlouhou. Doufám však, že bude užitečná. Bohužel si opět musím uvědomit, že jsem toho napsal příliš mnoho. Z tohoto důvodu byla část materiálu přesunuta do další části. Konkrétně se jedná o řízení stejnosměrných motorů. Toto téma je příliš důležité na to, abychom se jím zabývali jen krátce.

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

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

Další části kurzu jsou ukázkou používání textového displeje, který je součástí sady.

Přejít nahoru