Acrob: Rozdiel medzi revíziami

Zo stránky Robotický krúžok
Skočit na navigaci Skočit na vyhledávání
dBez shrnutí editace
dBez shrnutí editace
 
Riadok 1 246: Riadok 1 246:


<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
// subor: ultrazvuk.ino
// zapojenie: D2: pin TRIG ultrazvukoveho senzora
//            D3: pin ECHO ultrazvukoveho senzora
// popis: program vypisuje nameranu vzdialenost na seriovy port
#define TRIG 2
#define TRIG 2
#define ECHO 3
#define ECHO 3

Aktuálna revízia z 03:44, 1. február 2017

Robot Acrob

Od združenia Robotika.SK sme mali asi mesiac požičané roboty Acrob a na túto stránku sme zapísali všetko, čo sme s robotmi robili. Robot Acrob - používa dosku, ktorá sa podobá na Arduino a funguje rovnako.

Každý počítač (ako aj ATMega328 na Acrobovi) sa skladá z týchto základných častí:

základná schéma počítača

Pamäť uchováva všetky údaje (obrázky, zvuky, texty, čísla, a podobne) a programy, ktoré sa vykonávajú príkaz za príkazom v procesore. Pamäť s procesorom sú navzájom prepojené zbernicou, ktorá medzi nimi prenáša údaje. Aby s počítač mohol komunikovať so svojím okolím - napr. s človekom, alebo s vonkajšími pamäťami, na ktorých ukladá rozsiahle údaje (pevné disky, DVD, Internet), tak sú na zbernici pripojené aj vstupné a výstupné zariadenia.

To znamená, programy pozostávajú z postupnosti príkazov:

príkaz1
príkaz2
...
príkazN

V každom okamihu procesor naraz vykonáva iba jeden príkaz, postupne od začiatku programu do konca. V programoch sa môžu vyskytovať cykly:

príkaz
príkaz
...
opakuj:
  príkaz
  príkaz
koniec cyklu
príkaz
príkaz
...

- ak treba nejakú skupinu príkazov niekoľkokrát zopakovať, alebo podmienky, ktoré dovoľujú vykonať rozličné skupiny príkazov, podľa toho, ktorá z nich je práve vhodná (väčšinou na základe otestovania nejakej podmienky):

príkaz
príkaz
...
platí podmienka?
ak áno, vykonaj túto skupinu príkazov:
  príkaz
  ...
  príkaz
ak nie, vykonaj inú skupinu príkazov
  príkaz
  ...
  príkaz
a potom pokračuj v každom prípade ďalej:
príkaz
príkaz
...

Arduino je teda počítač, ktorý má svoju pamäť, procesor a nahrávame do neho cez kábel program, ktorý najskôr naprogramujeme pomocou prostredia Arduino:

program zapisovaný v prostredí Arduino.

Nato, aby sme mohli pracovať s prostredím Arduino si ho potrebujeme downloadnut a nainštalovať zo stránky arduino.cc - nainštalujte si "Windows Installer" - to by malo nainštalovať aj ovládače (drivers).

Program sa do Arduina nahráva cez prevodník USB-Serial (ktorý je v bežných arduinach už zabudovaný - ale Acroby ich potrebujú):

prevodník USB-Serial

Arduinové výstupy sú označené D0-D13 a A0-A5. Napájanie sa privádza na piny VCC a AVCC (+5V) a GND (zem, 0V). RXD, TXD označujú sériový port na komunikáciu s inými počítačmi alebo zariadeniami (napr. s PC), motory sa väčšinou pripájajú na piny D9 a D10.

Na väčšine Arduin je pin D13 pripojený na zabudovanú svietivú diódu (LED). Prvý program, ktorý napíšeme túto LEDku rozbliká.

Každý arduinový program obsahuje aspoň tieto dve funkcie:

 void setup() {
  // put your setup code here, to run once:
  //...
 }
 
 void loop() {
  // put your main code here, to run repeatedly:
  //...
 }

Funkcia setup() sa spustí vždy po zapnutí počítača - hneď po spustení programu. Jej úlohou je ponastavovať všetky zariadenia, ktoré sú k Arduinu pripojené. Funkcia loop() sa spustí, keď setup() skončí a ak loop() skončí (čo nemusí...), tak sa spúšťa znovu a znovu, stále dookola.

Pre rozblikanie LED musíme najskôr počítaču nastaviť pin D13 ako výstupný. Každý digitálny pin sa dá používať buď ako vstupný (vtedy Arduino z nožičky integrovaného obvodu vie prečítať, či tam je logická 0 (nulové napätie), alebo logická 1 (napätie 5V). Pin nemôže byť naraz aj vstupný aj výstupný, musíme sa rozhodnúť ako ho chceme používať a podľa toho ho vo fukcii setup() nastaviť. Robí sa to príkazom:

  pinMode(číslo_pinu, režim); // pričom režim je buď INPUT, alebo OUTPUT  (je možná aj tretia možnosť, ale o tom až niekedy neskôr)

Keď je pin nastavený ako výstupný, tak vo funkcii loop() môžeme LED kedykoľvek zasvietiť zapísaním logickej jednotky:

digitalWrite(číslo_pinu, HIGH);  // alebo  
digitalWrite(číslo_pinu, 1)  // čo je to isté.

podobne, LED zhasneme takto:

digitalWrite(číslo_pinu, LOW);   // alebo  
digitalWrite(číslo_pinu, 0)  // čo je to isté.

Arduino však stihne za sekundu vykonať 16 miliónov inštrukcií (taktovacia frekvencia procesora je 16 MHz) a tak, aby sme si blikanie LED stihli všimnúť, musíme po každej zmene pinu nejaký čas počkať - na to slúži funkcia delay(čas_v_milisekundách). Celý program na blikanie LED teda vyzerá takto:

 // subor: blikaj_ledkou.ino
 // popis: program blikne raz za sekundu zabudovanou LED 
 void setup() {
  pinMode(13, OUTPUT);
 }
 
 void loop() {
  digitalWrite(13, HIGH);
  delay(500);
  digitalWrite(13, LOW);
  delay(500);
 }

Aby sa nám Arduino programovalo jednoduchšie, ak ho máme pripojené káblom, môžeme z neho cez kábel (čiže cez sériový port) do PC poslať nejakú textovú správu:

Vo funkcii setup() najskôr nastavíme rýchlosť prenosu (počet prenesených bitov za sekundu), napr.

Serial.begin(115200);

a potom môžeme používať funkcie na vstup a výstup cez sériový port, napr.

Serial.println("Ahoj");

Celý takýto text sa odvysiela cez jediný pin TXD mikropočítača ATMega328 (pozri obrázok hore). Ako sa to udeje? Každý znak (písmeno, číslica, znamienko a pod.) má nejaký číselný kód - väčšinou sa na to používa medzinárodný štandard ASCII. Vidíme, že napríklad znak 'A' má kód 65, znak 'o' má kód 111, atď.

ASCII tabuľka

Tieto kódy sú v Arduine uložené v dvojkovej sústave, tak ako všetky ostatné čísla! Počítače totiž všetky čísla ukladajú v dvojkovej sústave, keďže na to stačí, aby na príslušnom pine bolo kladné napätie (logická jednotka) alebo nulové napätie (logická nula).

Ľudia na výpočty používajú desiatkovú sústavu, keďže majú 10 prstov, je im prirodzenejšia: obsahuje 10 číslic: 0, 1, 2, ..., 9. Za poslednou číslicou nasleduje číslo 10 zložené z dvoch číslic, za posledným dvojciferným číslo 99 nasleduje číslo 100, atď. V čísle, ktoré má viac číslic stojí každá číslica na nejakom mieste, napr. v čísle 2016: je číslica 6 na mieste jednotiek (má hodnotu 6 x 1), číslica 1 na mieste desiatok (má hodnotu 1 x 10), číslica 0 na mieste stoviek (hodnota 0 x 100) a číslica 2 na mieste tisícok (hodnota 2 x 1000). Podobne to funguje aj v dvojkovej sústave, ale namiesto 10 číslic máme k dispozícii iba číslice dve: 0 a 1. Čísla tam nasledujú za sebou takto:

    0      - zodpovedá 0 v desiatkovej sústave)
    1      - 1
   10      - 2
   11      - 3
  100      - 4
  101      - 5
  110      - 6
  111      - 7
 1000      - 8
 1001      - 9
 1010      - 10
 1011      - 11
 1100      - 12
 1101      - 13
 1110      - 14
 1111      - 15
10000      - 16
10001      - 17
 atď.

ľahko si všimneme, že aj tu majú číslice svoje "rády", napr. v čísle 10110 (čiže 22 v desiatkovej sústave) máme:

 0 - rád jednotiek 
 1 - rád dvojek (1 x 2) 
 1 - rád štvoriek (1 x 4)
 0 - rád osmičiek
 1 - ráď šestnástok (1 x 16)
1 x 2 + 1 x 4 + 1 x 16 = 22

Takže na odvysielanie čísla 8-bitového čísla 65 cez pin TXD sa tam postupne odvysielajú číslice 01000001 - a pri rýchlosti 115200 číslic za sekundu tam každá bude iba 1/115200 s, čo je približne 8,68 mikrosekúnd.

Predchádzajúci program upravme tak, aby okrem blikania LEDkou aj vypisoval texty ON a OFF:

 // subor: blikaj_a_vypisuj.ino
 // popis: program blikne raz za sekundu zabudovanou LED
 //        a pritom cez seriovy port vypisuje textovu spravu
 void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(115200);
 }
 
 void loop() {
  digitalWrite(13, HIGH);
  Serial.println("ON");
  delay(500);
  digitalWrite(13, LOW);
  Serial.println("OFF");
  delay(500);
 }

Dobrý robot by ale iba s výstupmi nevystačil, musí nejakým spôsobom získavať informácie z prostredia - pomocou senzorov, ktoré tvoria jeho vstupné zariadenia. Napríklad ak nárazník narazí do prekážky, stlačí sa mikrospínač a hodnota na výstupe z vypínača sa zmení z logickej nuly na logickú jednotku. Potrebujeme napísať program, ktorý hodnotu na svojom pine dokáže prečítať.

Pin 2 nastavíme na vstupný:

 pinMode(2, INPUT);

a funkcia

 digitalRead(2)

nám vráti buď 0 alebo 1 - podľa toho, či je na pine 2 logická nula, alebo jednotka. Túto hodnotu môžeme použiť napríklad v podmienke, ktorá sa v jazyku C++ (na ktorom je Arduino založené) používa takto:

 if (podmienka) {
   // príkazy, ktoré sa majú vykonať, ak je podmienka splnená
 }
 else {
   // príkazy, ktoré sa majú vykonať, ak podmienka nie je splnená, čiže neplatí
 }

časť else je nepovinná - môže a nemusí tam byť. V našom prípade môžeme program naprogramovať tak, že pri stlačení tlačidla LED zasvieti:

 // subor: zasviet_pri_stlaceni.ino
 // zapojenie: D0 - zem cez rezistor a zaroven:
 //            D0 - na mikrospinac za ktorym je VCC
 // popis: pri stlaceni mirkospinaca sa rozsvieti zabudovana LED
 void setup() 
 {
   pinMode(2,INPUT);
   pinMode(13, OUTPUT);
 }
 
 void loop() 
 {
   if (digitalRead(2)) {
     digitalWrite(13, HIGH);
   }
   else {
     digitalWrite(13,LOW);
   }
 }

Ako to zapojiť?

Mikrospínač, ktorý je v stavebnici Acroba funguje tak, že bez stlačenia sú prepojené len kontakty cez dlhšie strany obdĺžnika a po stlačení sú prepojené všetky 4 kontakty.

Microswitch.jpg

Preto do pinu D2 pripojíme jeden výstup z tlačidla. Druhý výstup tlačidla spojíme s napájaním (VCC), ktoré má hodnotu napätia ako logická 1. Tým zabezpečíme, že pri stlačení tlačidla bude na D2 logická jednotka. Lenže, ak tlačidlo nestlačíme - logická nula tam nebude, bude tam len pripojený "vo vzduchu visiaci" káblik a to nie je dobre. Preto ho treba prepojiť so zemou. Ale pozor - ak by sme ho so zemou prepojili len tak - priamo, tak by sme pri stlačení tlačidla spôsobili skrat - priamymi vodičmi by bolo spojené VCC (plus na baterke) so zemou (mínus na baterke) a takúto situáciu nesmieme dopustiť ani na krátky okamih. Preto pin D2 spojíme so zemou cez rezistor s dostatočne veľkým odporom, napr. 2 kOhm:

zapojenie s mikroswitchom

Robot Acrob je poháňaný dvoma servomotormi. Servomotor je zaujímavá súčiastka - je to motor s prevodovkou a riadiacou elektronikou. Vnútri vyzerá nejak takto:

vnútornosti servomotorov

používajú sa väčšinou v robotických ramenách, pričom sa môžu otáčať v rozsahu 0-180 stupňov (90 je stredná poloha). Viac otočiť im nedovolí zabudovaná zarážka. Potenciometer je v skutočnosti rezistor s premenlivou hodnotou a používa sa ako spätná väzba, ktorá riadiacu elektroniku serva informuje o tom, ako je práve servo otočené. Cez signálny káblik (biely, alebo žltý) elektronika dostáva pokyn z počítača (napr. z Arduina), do akej polohy sa má servo nastaviť. Elektronika porovná aktuálnu hodnotu s požadovanou a podľa toho rozbehne motor správnym smerom a vhodnou rýchlosťou.

Servá, ktoré sú zabudované do Acrobov, sú tzv. modifikované servá - zarážka, ktorá bráni súvislému otáčaniu je odstránená a potenciometer je nahradený rezistorom so zafixovanou - pevnou hodnotou. Elektronika serva si preto "myslí", že servo je stále v strede (na hodnote 90) a pri pokyne nastavenia do polohy napr. 120 sa roztočí jedným smerom a bude sa tak točiť donekonečna. Pri pokyne napr. 30 sa bude točiť donekonečna opačným smerom. Pri požadovenej polohe 90 sa zastaví a pri polohách blízko 90 sa bude otáčať iba pomaly, pretože už je skoro "v cieli".

Ako servo riadiť - ako mu vysvetliť, že sa má nastaviť do požadovanej polohy napr. 30 stupňov?

Riadiaci signál do serva (biely alebo žltý káblik) by mal každých 20 ms obsahovať pulz (logickú jednotku na krátky okamih). Dĺžka tohto okamihu určuje do akej polohy sa servo má nastaviť. Typicky to býva od 0.5 - 1.5 ms. Nasledujúci program teda roztočí servo jedným smerom:

 // subor: pohni_servom.ino
 // zapojenie: D10 - riadiaci signal pre servo (modifikovane)
 // popis: program toci modifikovanym servomotorom, 
 //        alebo sa bezne servo posunie do svojej krajnej polohy
 void setup() 
 {
   pinMode(10, OUTPUT);
 }
 
 void loop() 
 {
   digitalWrite(10, LOW);
   delay(20);
   digitalWrite(10, HIGH);
   delayMicroseconds(500);
 }

program vyšle na pin D10 krátky pulz 0.5 ms každých 20 ms. Toľko teória. Arduino je však vymyslené tak, aby sme sa nemuseli starať o každý bit sami a stačí použiť knižnicu Servo, ktorá sa o generovanie riadiaceho signálu stará sama v spolupráci so zabudovanými časovačmi počítača. Program, ktorý pohne robotom teda vyzerá takto:

 // subor: ukazka_pohybu.ino
 // zapojenie: D9 - riadiaci signal pre lave servo
 //            D10 - riadiaci signal pre prave servo
 // popis: robot do nekonecna opakuje pohyb vpred a zastavenie
 #include <Servo.h>
 
 Servo lavy, pravy; 
 
 void setup() 
 {
   lavy.attach(9);  // lave servo je na pine 9
   pravy.attach(10);  // prave servo je na pine 10
 } 
 
 void loop() 
 {
   // rozbehni sa
   lavy.write(120);
   pravy.write(30);
   // tri sekundy bez
   delay(3000);
   // zastan
   lavy.write(90);
   pravy.write(90);
   // tri sekundy cakaj
   delay(3000);
 }

K Acrobu môžeme pripojiť malú sirénku, ktorá funguje ako reproduktor:

malá signálna siréna

Sirénku si môžeme predstaviť ako taký malý reproduktor. Reproduktor vytvára zvuk tak, že po dvoch drôtoch doň prichádza premenlivé napätie pripojené na jeho cievku elektromagnetu. Elektromagnet je pevne spojený s kuželovitou kmitajúcou membránou a vytvorené magnetické pole ho striedavo priťahuje a odpudzuje k pevému magnetu, ktorý je tiež súčasťou reproduktora.

čo sú reproduktory?

Membrána reproduktora naráža na častice vzduchu, rozkmitá ich a zvuk sa v prostredí šíri ako pozdĺžne kmitajúca vlna mechanického vlnenia ďalej. Na vytvorenie čistého zvuku by sme mali napätie privádzané do reproduktora meniť plynule

plynule sa meniace napätie

ale pri využití digitálnej elektroniky, ktoré vie na výstupe generovať iba 0 a 1 si to môžeme zjednodušiť a pomocou pravidelného striedania digitálneho signálu medzi 0 a 1 môžeme vydať počuteľný tón. Dôležité je hlavne to, aby frekvencia kmitania sedela s frekvenciou požadovaného tónu.

digitálna aproximácia sinusoidy

Treba dať pozor na pripojenie - jedna strana sirénky je ozačená ako "+" - tú môžeme pripojiť priamo na niektorý digitálny výstupný pin a riadiť ho rovnako ako pri blikaní LEDkou, len trochu rýchlejšie, druhú stranu sirénky pripojíme priamo na zem.

Hudobné tóny rozličných výšok sa líšia svojimi frekvenciami. Tón s veľkou frekvenciou (za sekundu membrána kmitne veľakrát) je vysoký tón, naopak tón s nízkou frekvenciou kmitania membrány je nízky tón. Človek typicky počuje frekvencie okolo 20-15000 Hz. Nasledujúci program teda vydá tón malé 'A', ktorý má frekvenciu 440 Hz:

 // subor: zajraj_a.ino
 // zapojenie: D11 - plus pin sirenky (jej minus ide na zem)
 // popis: suvisle pipa frekvenciou 440 Hz (ton male A)
 void setup() 
 {
   pinMode(11,OUTPUT);
 }

 void loop() 
 {
   digitalWrite(11, HIGH);
   delayMicroseconds(1136);     // 1136 usec je dlzka trvania jednej pol-periody
   digitalWrite(11,LOW);        // ak kmitame 440-krat za sekundu, tak dlzka jedneho
   delayMicroseconds(1136);     // kmitnutia je 1/440 s, co je 0.0022727 s, polovica
 }                              // z toho je 0.0011363 s = 1.1363 ms = 1136.3 usec

Ak chceme, aby robot zahral nejakú zaujímavejšiu pesničku, tak by sa nám hodila funkcia, ktorá vie zahrať tón s ľubovoľnou frekvenciou a bolo by fajn, keby sme tej funkcii vedeli povedať ako dlho ten tón znie. Funkcia? a to je čo? Funkciu si môžeme predstaviť ako "vlastný blok" v programovacom jazyku pre LEGO MINDSTORMS. Je to kus programu, ktorý môžeme vyvolať opakovane z rôznych miest nášho programu - a môžeme mu zadať nejaké parametre, ktoré určujú, čo presne má urobiť. Každý náš arduinovský program už pozostával z dvoch funkcií - setup() a loop(). Takýchto funkcií môžeme pridať koľko chceme a vyvolať ich z hociktorého miesta programu. Funkcii môžeme predpísať ľubovoľný počet parametrov - každý z nich musí mať stanovený "typ" - čiže napr. či je to číslo, logická hodnota, znak, alebo reťazec znakov. Napríklad:

 void scitaj(int a, int b)
 {
   Serial.print("Sucet je: ");
   Serial.println(a + b);
 }

je funkcia, ktorá dostane dva parametre a, b, a cez sériový port vypíše text "Sucet je XY", kde XY bude skutočný súčet hodnôt a, b. Takúto funkciu môžeme z programu vyvolať napr. takto:

 scitaj(3, 4);

ale za parametre môžeme dosadiť aj hodnoty z premenných, napr.

 int premenna = 12;
 int ina_premenna;
  
 ina_premenna = 10;
 
 scitaj(premenna, ina_premenna);

Takže teraz napíšme funkciu, ktorá bude zadanou frekvenciou (určenou v Hz) kmitať stanovenú dobu (určenú v ms). Namiesto typu int použijeme typ long, lebo do premenných typu long sa vojdú oveľa väčšie čísla - čo potrebujeme, lebo budeme počítať v mikrosekundách (miliontinách sekundy).

 // f - frekvencia v Hz, d - dlzka trvania tonu v ms
 void ton(long f, long d)
 {
   long t = 500000 / f;     // ako dlho trva polovica jednej periody tonu s frekvenciou f? 
   long p = d * f / 1000;   // kolko celych period sa vojde do casoveho useku dlzky d?
   for (int i = 0; i < p; i++) 
   {
     digitalWrite(11, HIGH);
     delayMicroseconds(t);  // namiesto 1136 usec ako v pripade tonu s frekvenciou 440 Hz 
     digitalWrite(11,LOW);  // budeme cakat t usec, ktore sme vypocitali z frekvencie f
     delayMicroseconds(t);  //  1 / f - v sekundach, takze este krat milion (na mikrosekundy) a deleno 2
   }                        // co je to iste ako 500000 / f
   delay(50);  // na konci kratka prestavka, aby boli tony zretelne oddelene a nezlievali sa
 }

V tomto programe sú dva zaujímavé výpočty. Prvý z nich využíva fakt, že vzťah medzi frekvenciou a periódou je T = 1 / f a zároveň f = 1 / T. V prvom riadku funkcie teda vypočítame koľko mikrosekúnd trvá polovica periódy, ak sa má za jednu sekundu zopakovať f kmitnutí signálu nahor a nadol. Druhý výpočet slúži na to, aby sme vedeli koľkokrát sa má kmitnutie nahor a nadol zopakovať (to je tá podmienka i < p v cykle for). Zistíme to tak, že vynásobíme dĺžku trvania tónu (d) frekvenciou (f). Napríklad ak kmitáme frekvenciou 440 Hz a tón má znieť 1 sekundu, tak potrebujeme kmitnúť 1 * 440 = 440-krát. Alebo ak má tón znieť 2 sekundy, tak potrebujeme kmitnúť 2 * 440 = 880-krát. Ak by mal znieť iba pol sekundy, tak by to bolo 0,5 * 440 = 220-krát. Lenže dĺžka trvania tónu (d) je zadaná v milisekundách, takže aby sme dostali správny výsledok, musíme ho ešte vydeliť 1000 (čím ako keby prevedieme hodnotu d na milisekundy). Cyklus for, ktorý sme použili je najbežnejšie používaný cyklus, pomocou ktorého môžeme nejakú skupinu príkazov uzavretú v zložených zátvorkách zopakovať viackrát. Zapisuje sa takto:

 for (príkaz_ktory_sa_vykona_na_zaciatku; podmienka_opakovania; prikaz_ktory_sa_vykona_po_kazdom_opakovani)
 {
   //...prikazy...
 }

Prikaz for teda vŽdy vykoná najskôr svoj prvý príkaz za zátvorkou - v našom prípade vytvorí premennú typu int a nastaví ju na hodnotu 0. Potom skontroluje, či platí podmienka opakovania - v našom prípade, či počet opakovaní (premenná i) je stále menší ako stanovený počet opakovaní (premenná p) - ak áno, prvýkrát vykoná príkazy v tele cyklu (medzi zloženými zátvorkami). Potom nasleduje príkaz, ktorý sa vykoná po každom opakovaní cyklu - posledný výraz v okrúhlych zátvorkách za slovíčkom for - v našom prípade "i++", čo znamená zvýšiť premennú i o jedna a následne sa zasa skontroluje, či stále platí podmienka opakovania. Ak nie, cyklus skončil a program pokračuje ďalším príkazom za telom cyklu. Ak je podmienka stále splnená, znovu sa vykonajú príkazy v tele cyklu, príkaz po každom opakovaní a zasa sa otestuje podmienka - a takto až dovtedy, kým podmienka prestane byť splnená (čo v krajnom prípade nemusí byť nikdy - v takom prípade cyklus nikdy neskončí).

Keď máme funkciu hotovú, môžeme ju využiť na to, aby robot zahral známu melódiu Kohútik jarabí :-) C-D-E-F-F-F-F-E-D-E-E-E-E-D-C-D-D-D-D-E-D-C-C-C s dĺžkami nôt 1-1-2-2-1-1-1-1-2-2-1-1-1-1-2-2-1-1-1-1-2-2-1-1, kde 1 je polová a 2 je celá nota (polová má polovičnú dobu trvania):

Aby to bolo zaujímavejšie na digitálne výstupy D0, D1, D2, D3 môžeme zapojiť rozličné LEDky a spolu s jednotlivými tónmi C,D,E,F zapínať jednotlivé LED - získame takto farebnú hudbu :-)

farebná hudba na robotovi Acrob

 // subor: kohutik.ino
 // zapojenie: D11 - plus pin sirenky (minus ide na zem)
 // popis: zahra melodiu pesnicky Kohutik jarabi
 void ton(long frekvencia, long trvanie)
 {
   long p = trvanie * frekvencia / 1000L;   // L na konci cisla zdoraznuje
   long polPeriody = 500000L / frekvencia;  // ze ide o konstantu typu long, nemusi tam byt
 
   for (int i = 0; i < p; i++)
   {
     digitalWrite(11, HIGH);
     delayMicroseconds(polPeriody);
     digitalWrite(11, LOW);
     delayMicroseconds(polPeriody);
   }
   delay(50);
 }
 
 void setup() 
 {
    pinMode(11, OUTPUT);  // pipac
    
    pinMode(0, OUTPUT);   // farebna hudba - 4 LED
    pinMode(1, OUTPUT);
    pinMode(2, OUTPUT);
    pinMode(3, OUTPUT);   
 }
 
 // dostane cislo LED 1-4, tu zasvieti a ostatne zhasne
 void led(int ktora)
 {
   if (ktora == 1) digitalWrite(0, HIGH);
   else digitalWrite(0, LOW);
   if (ktora == 2) digitalWrite(1, HIGH);
   else digitalWrite(1, LOW);
   if (ktora == 3) digitalWrite(2, HIGH);
   else digitalWrite(2, LOW);
   if (ktora == 4) digitalWrite(3, HIGH);
   else digitalWrite(3, LOW);
 }
 
 // C: 262
 // D: 293
 // E: 330
 // F: 349
 void hrajC(int dlzka)
 {
   led(1);
   ton(262, dlzka);  
 }
 
 void hrajD(int dlzka)
 {
   led(2);
   ton(293, dlzka);  
 }
 
 void hrajE(int dlzka)
 {
   led(3);
   ton(330, dlzka);  
 }
 
 void hrajF(int dlzka)
 {
   led(4);  
   ton(349, dlzka);
 }
 
 void loop() 
 { 
   hrajC(500);
   hrajD(500);
   hrajE(1000);
   hrajF(1000);
   hrajF(500);
   hrajF(500);
   hrajF(500);
   hrajE(500);
   hrajD(1000);
   hrajE(1000);
   hrajE(500);
   hrajE(500);
   hrajE(500);
   hrajD(500);
   hrajC(1000);
   hrajD(1000);
   hrajD(500);
   hrajD(500);
   hrajD(500);
   hrajE(500);
   hrajD(1000);
   hrajC(1000);
   hrajC(500);
   hrajC(500); 
 }

Môže byť? Neviem, asi by bolo ešte lepšie, ak by sme melódiu nemuseli zapisovať takýmto spôsobom. Jednak tu máme iba funkcie na 4 tóny a na klavíri býva aj viac ako 80 klávesov a tak vytvárať funkciu pre každý tón nebude veľmi praktické. Skúsme to ešte trochu lepšie - použijeme pole.

Využijeme pole na to, aby sme si uložili frekvencie všetkých tónov, ktoré bude náš muzikantský program poznať. Pole je taká premenná, do ktorej sa dá uložiť viacero hodnôt naraz, nielen jedna. Napríklad

 int a[3];  // premenna a schovava tri cele cisla
 
 a[0] = 10; // prve z nich nech je 10
 a[1] = 11; // do druheho vloz 11
 a[2] = 12; // a posledne nech obsahuje 12
 
 int b = a[0] + a[1] + a[2];  // vypocitaj sucet vsetkych troch hodnot a uloz ho do premennej b
 
 Serial.print("Sucet je: ");  // vypis vysledok na seriovy port
 Serial.println(b);

na prístup k jednotlivým hodnotám v poli môžeme použiť aj premennú (niekedy sa jej hovorí indexová, lebo poradie prvku, ku ktorému sa pristupuje sa volá index):

 int a[10];
 
 for (int i = 0; i < 10; i++)   
 {
   a[i] = 1 + i * 2;   
 }
 
 int sucet = 0;
 for (int i = 0; i < 10; i++)
 {
   Serial.print(a[i]);
   if (i < 9) Serial.print("+");
   sucet = sucet + a[i];
 }
 Serial.print("=");
 Serial.println(sucet);

Zistite, čo presne urobí tento program?

Tak teda naspäť k nášmu hodobníckemu programu.

 // subor: melodia.ino
 // zapojenie: D11 - plus pin sirenky (minus ide na zem)
 // popis: zahra melodiu zadanu v poliach melodiaT - tony
 //        a melodiaD - dlzky tonov
 
 #define TEMPO 24    // cim vyssia hodnota, tym pomalsie tempo
 
 // pocet not v melodii
 int dlzka = 25;  
 
 // tony v melodii
 char melodiaT[]= {'c', 'e', 'c', 'e', 'g', 'g', 'c', 'e', 'c', 'e', 'g', 'g', 
                   'C', 'h', 'a', 'g', 'f', 'g', 'a', 'g', 'f', 'e', 'd', 'c', 'c' };
 
 // dlzky not v melodii 16 - cela, 8 - polova, 4 - stvrtova, 2 - osminova, 1 - sestnastinova
 int melodiaD[] = { 8, 8, 8, 8, 16, 16, 8, 8, 8, 8, 16, 16,
                     8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 16, 16}; 
 
 // ake tony pozname - pouzili sme specialne znaky na oznacenie poltonov, zatial nam stacia 2 oktavy 
 char tony[] = {'c', '#', 'd', '%', 'e', 'f', '$', 'g', '^', 'a', '*', 'h',
                'C', '!', 'D', '~', 'E', 'F', '+', 'G', '-', 'A', '/', 'H' };
 
 // hodnoty frekvencii tonov a poltonov sme si nasli na internete:
 // radkon.eu/projects/other/tones.html
 
 int frekvencie[] = {261, 277, 293, 311, 329, 349, 369, 391, 415, 440, 466, 493, 523, 554, 587, 622, 659, 698, 739, 783, 830, 880, 932, 987};
 
 void setup() 
 {
   pinMode(11, OUTPUT);                    // sirenku pripojime na D11
   
   // raz zahraj celu melodiu
   for (int i = 0; i < dlzka; i++)
     hraj(melodiaT[i], melodiaD[i]);
 }
 
 void ton(long f, long d)
 {
   long p = d * f / 1000L;
   long polPeriody = 500000L / f;
 
   for (int i = 0; i < p; i++)
   {
     digitalWrite(11, HIGH);
     delayMicroseconds(polPeriody);
     digitalWrite(11, LOW);
     delayMicroseconds(polPeriody);
   }
   delay(50);
 }
 
 void hraj(char nota, int trvanie)
 {
  int kde = 0;
  while (tony[kde] != nota) kde++;  // najdeme index pozadovaneho tonu
  ton(frekvencie[kde], trvanie * TEMPO);  // a zahrame ho
 }
                 
 void loop() 
 {
   // melodiu zahrame priamo vo funkcii setup, aby sa po resete zahrala iba raz a nehrala stale dookola
 }

Aká je to pesnička? Kto naprogramuje vianočnú melódiu? :-)

Odpoveď Sama: Pesnička je to "jedna druhej riekla keď koláče atď." a pesničku by som naprogramoval aj ja.

A potom pridal túto:

 // tony v melodii
 char melodiaT[]= {'E', '~', 'E', '~', 'E', 'h', 'D', 'C', 'a', 'c', 'e', 'a', 
                   'h', 'e', '^', 'h', 'C'};
 
 // dlzky not v melodii 16 - cela, 8 - polova, 4 - stvrtova, 2 - osminova, 1 - sestnastinova
 int melodiaD[] = { 8, 8, 8, 8, 8, 8, 8, 8, 16, 8, 8, 8,
                     16, 8, 8, 8, 16};

Nasledujúci program svieti LEDkou rozličnou silou - pomocou znakov '+' a '-' prijatých cez sériový port sa sila svietenia dá zvýšiť alebo znížiť.

// subor: led_plynulo.ino
// popis: cez seriovy port pouzivatel posiela znaky + a -, cim riadi 
//        intenzitu svietenia zabudovanej LED (ktora v skutocnosti len
//        rozne dlho blika, ale to ludske oko nezachyti)
void setup() {  
  pinMode(13, OUTPUT);
  Serial.begin(115200);
  Serial.println("Som pripraveny.");
}

int text;
int dlzka1 = 2000;

void loop() {
  if (Serial.available() > 0) {
    text = Serial.read();
    if ((text == '+') && (dlzka1 < 2000)) {
      dlzka1 = dlzka1 + 50;
      Serial.println(dlzka1);
    }
  
    if ((text == '-') && (dlzka1 > 0)) {
      dlzka1 = dlzka1 - 50;
      Serial.println(dlzka1);
    }
  }
  digitalWrite(13,HIGH);
  int dlzka2 = 2000 - dlzka1;
  delayMicroseconds(dlzka1);
  digitalWrite(13,LOW);
  delayMicroseconds(dlzka2);
}

Na precvičenie sme si napísali program, ktorý vypíše na sériový port čísla 0-1000 v dvojkovej sústave:

 // subor: vypis_binarne.ino
 // popis: na seriovy port sa vypisu cisla 0-1000 v dvojkovej sustave
 void setup() {
   Serial.begin(115200);
   for (int cislo = 1; cislo < 1001; cislo ++) {
     Serial.print(cislo, BIN);  // druhy nepovinny argument urcuje sustavu v ktorej
     Serial.print(" = ");       //  sa cislo vypisuje, BIN znamena dvojkovu, HEX - sestnastkovu,
     Serial.println(cislo);     //  DEC - desiatkovu a OCT osmickovu
     Serial.println();
   }
 }

void loop() {
  // cisla vypiseme iba raz priamo vo funkcii 
  // setup(), aby sa vypisali len raz
}

SHARP IR senzor

Sharp ir sensor.jpg

Ide o analógový senzor, to znamená, že na svojom výstupe nedáva iba hodnoty 0 (0 Voltov) alebo 1 (5 Voltov), ale ľubovolnú hodnotu napätia medzi 0 a 5V. Arduino (teda jednočipový mikropočítač ATmega328) má zabudovaný prevodník, ktorý dokáže analógový signál previesť na digitálny (na 10-bitovú hodnotu 0-1023). Začnime programom, ktorý iba vypisuje hodnoty analógového senzora (pripojili sme ho na vstup A5) na sériovom porte (aj so Samovými vylepšeniami):

// subor: sharp_ir.ino
// zapojenie: A5 - pripojime signalny kablik zo SHARP IR senzora
// popis: na seriovom porte neustale vypisuje hodnotu precitanu
//        zo senzora a komentuje ju podla odhadovanej vzdialenosti
void setup() {
  Serial.begin(115200);
}

int sila = 0;
void loop() {
  sila = analogRead(5);
  Serial.println(sila);
  delay(250);
  if (sila > 250 && sila <= 300) {
    Serial.println("Pozor! Blizim sa k nejakemu predmetu!");
  }
  if (sila < 50) {
    Serial.println("Pokoj, moj senzor nevidi takmer nic.");
  }
  if (sila > 300) {
    Serial.println("Hej! Takmer som donho narazil!!!");
  }
}

Pomocou nasledujúceho programu sa robot pohybuje vpred, až kým nepríde k prekážke, ktorú zameria analógovým senzorom SHARP 2Y0A21 na meranie vzdialenosti (pracuje na princípe infračerveného svetla). Pri prekážke sa otáča, až kým nie je voľná cesta.

// subor: prieskumnik.ino
// zapojenie: D9  - lave servo
//            D10 - prave servo
//            A5  - signalny kablik z sharp ir senzora
// popis: robot so senzorom umiestnenym vpredu sa pohybuje neustale
//        vpred, kym nepride k prekazke, kde zmeni smer a znovu pokracuje
#include <Servo.h>

Servo lavy;
Servo pravy;
int dialka = 0;

void setup() 
{
  lavy.attach(9);
  pravy.attach(10);
}

void vpravo(int ms) {
  lavy.write(60);
  pravy.write(60);
  delay(ms);
  lavy.write(90);
  pravy.write(90);
}

void vlavo(int ms) {
  lavy.write(120);
  pravy.write(120);
  delay(ms);
  lavy.write(90);
  pravy.write(90);
}

void rovno(int ms) {
  lavy.write(60);
  pravy.write(120);
  if (ms>0) {
    delay(ms);
    lavy.write(90);
    pravy.write(90);
  }
}

void vzad(int ms) {
  lavy.write(120);
  pravy.write(60);
  if (ms>0) {
  delay(ms);
  lavy.write(90);
  pravy.write(90);
  }
}

void loop() 
{
  dialka = analogRead(5);
  
  if (dialka < 300) {
    rovno(0);
  }
  else {
    vlavo(250);
  }
}

Senzor na čiaru pracuje veľmi podobne - tiež je to analógový senzor, ale nie je ešte celý, potrebuje ešte jeden rezistor veľkosti 10kOhm medzi červený a biely (niekedy je žltý) káblik. Presné zapojenie si požičiame zo stránky o robotovi Acrob:

Zapojenie senzora na čiaru

Sledovanie čiary

Podľa obrázka sme pripojili dva svetelné senzory na piny A1 a A3. Najkôr by sme senzory chceli vyskúšať a zmerať, aké hodnoty z nich prichádzajú v rôznych situáciách - keď je senzor nad čiernou čiarou a keď je nad svetlou podložkou. Použijeme jednoduchý program, ktorý neustále číta hodnotu z analógového portu A1 a vypisuje ju na sériový port:

// subor: zobraz_ciarovy.ino
// zapojenie: A1 - signalny pin svetelneho senzora 
//                 (medzi VCC a signalnym pinom je 10k rezistor)
// popis: program neustale vypisuje hodnotu analogoveho signalu A1

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

int svetlo = 0;
void loop() {
  svetlo = analogRead(1);
  Serial.println(svetlo);
  delay(250);
}

Zistili sme, že kým je senzor nad stolom, hodnota je okolo 50-60, ak je nad čiernou čiarou, hodnota je až okolo 800-900. Naším cieľom bude napísať program, pomocou ktorého sa robot bude pohybovať po čiare, ktorá je zakrivená rôznymi smermi.

Acrob jazdi po ciare.jpg

Ak máme dva senzory, postačia nám tri rôzne stavy:

  • ľavý senzor vidí čiaru - to znamená, že robot je príliš vpravo a mal by sa začať otáčať vľavo
  • pravý senzor vidí čiaru - vtedy je robot príliš vľavo, mal by otáčať vpravo.
  • ani jeden senzor nevidí čiaru - to znamená, čiara by mala byť medzi dvoma senzormi, robot sa pohybuje vpred

Využijeme teda príkaz podmienky - if ... else ... ale potrebujeme rozlíšiť nielen dva, ale až tri prípady. Ako to dosiahneme?

if (lavy_senzor_vidi_ciaru) 
{
  pohybuj sa vlavo
}
else  // zostavajuce dva pripady)
{
  if (pravy_senzor_vidi_ciaru)
  {
     pohybuj sa vpravo
  }
  else   // posledny pripad, cize ani jeden senzor nevidel ciaru
  {
     pohybuj sa rovno
  }
}

Môžeme využiť funkcie vlavo(int ms), vpravo(int ms), rovno(int ms), ktoré riadia pohyb robota a ktoré sme naprogramovali minule. Tu je výsledný Samov program:

// subor: sleduj_ciaru.ino
// zapojenie: D9 - lave servo
//            D10 - prave servo
//            A1 - analogovy svetelny senzor pravy
//            A3 - analogovy svetelny senzor lavy
// popis: sleduje ciaru pomocou dvoch senzorov - ciara sa neustale
//        nachadza medzi senzormi, ak niektory zo senzorov zosnima
//        ciaru, robot uhne tak, aby sa ciara dostala znovu medzi
//        senzory.  ak sa vam robot pohybuje opacnym smerom,
//        alebo zataca opacnym smerom, skuste vymenit motory,
//        alebo zamenit hodnoty 60 a 120 vo funkciach vlavo(), vpravo()
#include <Servo.h>

Servo lavy;
Servo pravy;

void setup() 
{
  lavy.attach(9);
  pravy.attach(10);
}

void vpravo(int ms) {
  lavy.write(60);
  pravy.write(60);
  delay(ms);
  lavy.write(90);
  pravy.write(90);
}

void vlavo(int ms) {
  lavy.write(120);
  pravy.write(120);
  delay(ms);
  lavy.write(90);
  pravy.write(90);
}

void rovno(int ms) {
  lavy.write(120);
  pravy.write(60);
  if (ms>0) {
    delay(ms);
    lavy.write(90);
    pravy.write(90);
  }
}
  
void loop() 
{ 
  int lavysenzor = analogRead(3);
  int pravysenzor = analogRead(1);  
  if (lavysenzor>500) {
    vlavo(175); 
  }
  else if(pravysenzor>500) {
    vpravo(175);
  }
  else {
    rovno(0);
  }  
}

Doteraz náš robot komunikoval s okolím iba cez USB kábel, ktorý bol cez prevodník pripojený na jeho sériový port. V situáciách, keď sa robot pohybuje v nejakom prostredí a plní pritom svoju úlohu, pripojenie káblom môže byť nepraktické. Namiesto kábla môžeme využiť bezdrôtové spojenie cez BlueTooth, ktoré dokáže prenášať sériovú komunikáciu tiež. Použijeme BT modul s označením HC-05.

BT HC 05.jpg

Jedna nevýhoda modulu HC-05 je, že pracuje na logike s napätím 3.3 V a robot pracuje na logike 5 V. Preto signálne kábliky, cez ktoré sa prenášajú logické nuly a jednotky nemôžeme vždy priamo prepojiť. Presnejšie: v niektorých prípadoch výstupné signály na logike 3.3 V môžeme pripojiť do vstupných portov s 5 V logikou, lebo 3.3 V väčšinou stačí vybudiť aj 5 V logiku, našťastie v prípade obvodu ATmega328, ktorý je v Acrobovi stačí aj 3.3 V signál na vybudenie logickej 1, takže informácie prichádzajúce z PC cez BT do Acroba môžeme prepojiť priamo: pin Tx na HC-05 prepojíme s pinom D0 na Acrobovi (na ňom je pin RxD - vstupný signál serióvého portu). Opačným smerom to však nie je bezpečné, lebo 5V signál sa väčšinou do 3.3V logiky pripájať nesmie (pokiaľ to nie je zvlášť uvedené v dokumentácii - tzv. "5V tolerant pin" - to ale nie je prípad obvodu HC-05). V nasledujúcom príklade teda použijeme iba jednosmernú komunikáciu z PC do Acroba a Acrob nebude môcť do PC cez sériový port nič vypisovať. Aby to nebolo až také jednoduché, tak na module HC-05 je vyvedený pin EN (enable), ktorý však musíme nastaviť na logickú 1 (tých 3.3 V), aby bol modul aktivovaný a pracoval. 3.3 V si vyrobíme pomocou odporového deliča: ak dva rezistory zapojíme do série, tak sa napätie na tejto vetve obvodu rozdelí podľa pomeru odporov jednotlivých dvoch rezistorov. Použijeme rezistory 1kOhm a 2kOhm takto:

Odporovy delic.png

tým pádom získame napätie 3.33V, ktoré je už dostatočne blízko požadovaných 3.3V a pripojíme ho na pin EN. Nezabudneme pripojiť napájanie - 5V (VCC) na VCC modulu HC-05 a prepojiť zem (GND) Acroba a BT modulu.

BT modul využijeme na naprogramovanie diaľkovo riadeného robota. Cez sériový port z programu Putty budeme znakmi riadiť smer pohybu robota - vľavo, vpravo, rovno, vzad, zastaviť stáť.

Samo Acrob a BT.jpg

Do funkcií vlavo(int ms) a vpravo(int ms) doplníme možnosť zadať čas 0 ms, ktorý zodpovedá neohraničenému pohybu (ako v LEGO MINDSTORMS - "unlimited") tak ako to bolo vo funkciách rovno(int ms) a vzad(int ms). Modul HC-05 je z výroby nastavený na nízku komunikačnú rýchlosť 9600, aj keď v prípade potreby sa dá prestaviť na vyššiu (neskôr sa k tomu vrátime). Tu je výsledný program:

// subor: riadenie_cez_bt.ino
// zapojenie: D0 - TxD pin BT modulu HC-05 (alebo HC-06)
//            D9 - lave servo
//            D10 - prave servo
// popis: program reaguje na znaky w,d,a,medzera prijate zo serioveho
//        portu - kam mozeme pripojit BT modul. Jednotlive znaky riadia 
//        pohyb robota
#include <Servo.h>

Servo lavy;
Servo pravy;

void setup() 
{
  Serial.begin(9600);
  lavy.attach(9);
  pravy.attach(10);
}

void vpravo(int ms) {
  lavy.write(60);
  pravy.write(60);
  if (ms>0) {
    delay(ms);
    lavy.write(90);
    pravy.write(90);
  }
}

void vlavo(int ms) {
  lavy.write(120);
  pravy.write(120);
  if (ms>0) {
    delay(ms);
    lavy.write(90);
    pravy.write(90);
  }
}

void rovno(int ms) {
  lavy.write(120);
  pravy.write(60);
  if (ms>0) {
    delay(ms);
    lavy.write(90);
    pravy.write(90);
  }
}

void vzad(int ms) {
  lavy.write(120);
  pravy.write(60);
  if (ms>0) {
    delay(ms);
    lavy.write(90);
    pravy.write(90);
  }
}

void loop() 
{
 if (Serial.available() > 0) {
    int text=Serial.read();
    if (text == 'w') {
      rovno(0);
    }
    else if(text == 'd') {
      vlavo(0);
    }
    else if(text == 'a') {
      vpravo(0);
    }
    else if(text == ' ') {
      lavy.write(90);
      pravy.write(90);
    } 
  }
}

Dokázali by sme urobiť program, ktorý by vedel prejsť bludiskom? :-) Neskúsime sa zamyslieť nad robotom do kategórie Micromouse? Najskôr sa naučíme pracovať s programom OpenSCAD na vytváranie 3D modelov, ktoré sa dajú vytlačiť na 3D tlačiarni.

Využitie viacerých senzorov súčasne

v našich predchádzajúcich programoch sme využívali buď svetelné senzory na pohyb po čiare, alebo IR senzor na meranie vzdialenosti. V nasledujúcom programe to spojíme dokopy - robot sa má pohybovať po čiare iba dovtedy, kým má pred sebou voľno. Ak zachytí prekážku, má zastať a počkať, kým prekážka nezmizne. Potom má pokračovať v sledovaní čiary ďalej - až kým nepríde k ďalšej prekážke...

Tu je výsledok, ktorý vytvoril Ružový Panter:

// subor: ciara_a_prekazka.ino
// zapojenie: D9 - lave servo
//            D10 - prave servo
//            A5 - sharp IR senzor
//            A1 - lavy svetelny senzor
//            A2 - pravy svetelny senzor
// popis: program sleduje ciaru, kym sa pred nim nenachadza prekazka
//        pred prekazkou zastane a zostane stat, kym prekazka nezmizne,
//        potom pokracuje v sledovani ciary dalej
#include <Servo.h>

Servo lavy;
Servo pravy;
int sila = 0;

void setup() 
{
  lavy.attach(9);
  pravy.attach(10);
}

void vpravo(int ms) {
  lavy.write(60);
  pravy.write(60);
  delay(ms);
  lavy.write(90);
  pravy.write(90);
}

void vlavo(int ms) {
  lavy.write(120);
  pravy.write(120);
  delay(ms);
  lavy.write(90);
  pravy.write(90);
}

void rovno(int ms) {
  lavy.write(60);
  pravy.write(120);
  if (ms>0) {
    delay(ms);
    lavy.write(90);
    pravy.write(90);
  }
}
  
void loop() 
{ 
  sila = analogRead(5);            // tu vidime, ze premenne mozu byt delkarovane
  int lavysenzor = analogRead(1);  // bud mimo funkcie - ako premenna sila, alebo
  int pravysenzor = analogRead(2); // priamo vo funkcii ako lavysenzor, pravysenzor 
  if (lavysenzor>420) {            // v tom pripade su pouzitelne iba v tej funkcii
    vlavo(175); 
  }
  else if(pravysenzor>350) {
    vpravo(175);
  }
  else {
    rovno(0);
  }  
  while (sila > 300) {
     sila = analogRead(5);
    lavy.write(90);
    pravy.write(90);
  }
}

Sledovanie čiary pomocou dvoch svetelných senzorov je pomerne spoľahlivé, ale v prípade veľmi prudkých zákrut, alebo križovatiek sa situácia začína komplikovať. Viac informácie robot získa, ak namiesto dvoch senzorov použije väčší počet. Napríklad senzor Pololu QTR-8RC obsahuje 8 svetelných senzorov jeden vedľa druhého. Keďže len máloktoré jednočipové mikropočítače majú až tak veľa analógových vstupov, autori tohto senzoru vymysleli zaujímavý trik, ako poslať analógovú hodnotu cez digitálny pin. Senzor funguje takto:

Qtr 8rc.png

  1. príslušný digitálny pin (je ich spolu 8 lebo súčiastka má až 8 svetelných senzorov) najskôr prepneme do režimu výstupu (pinMode(X, OUTPUT)) a zapíšeme naň logickú jednotku (digitalWrite(X, HIGH)
  2. počkáme aspoň 10 mikrosekúnd, aby sa senzor "nabil"
  3. prepneme digitálny pin na vstupný (pinMode(X, INPUT)) a začneme ho merať
  4. najskôr bude na pine logická 1, ale po nejakom čase t ju senzor prepne na logickú nulu

Zmeraný čas t zodpovedá nameranej intenzite svetla odrazeného do senzora.

Našou úlohou bude naprogramovať čítanie z tohto zaujímavého senzora - zatial nám bude stačiť jeden z 8 pinov, aby sme si overili, že čítanie funguje správne:

// subor: citaj_jeden_qtr8rc.ino
// zapojenie: D3 - jeden z digitalnych pinov senzora QTR-8RC
//            D12 - pin LED EN senzora QTR-8RC, ktorym zapiname LED
// popis: program cita intenzitu odrazeneho svetla z jedneho pinu senzora QTR-8RC

void setup()
{
  Serial.begin(115200);
  pinMode(12, OUTPUT);    
  digitalWrite(12, OUTPUT);
}

void loop()
{
  pinMode(3, OUTPUT);
  digitalWrite(3, HIGH);
  delayMicroseconds(10);
  pinMode(3, INPUT);
  int cas = 0;
  while (digitalRead(3) == HIGH)
  {
    cas++;   // to je to iste ako: cas = cas + 1;
  }
  Serial.println(cas);
}

Vedeli by sme napísať program, ktorý číta naraz zo všetkých 8 pinov senzora?

Tu je:

// subor: ciara_qtr8rc.ino
// zapojenie: D2-D8: 7 digitalnych pinov senzora QTR-8RC
//            D12 - pin LED EN senzora QTR-8RC, ktorym zapiname LED
// popis: program jazdi po ciare - ak ciara zataca rychlo, zataca prudko,
//        inak len postupne. Ak je na ciare, robot sa pohybuje rovno.
//        vyuziva iba 7 z osmich senzorov QTR-8RC (z praktickych dovodov)
#include <Servo.h>
#define CIARA 90
Servo lavy, pravy;
void setup()
{ 
  pravy.attach(9);
  lavy.attach(10);
  Serial.begin(115200);
  pinMode(12, OUTPUT);
  digitalWrite(12, HIGH);
}

void loop()
{ 
  int cas[9];
  int kolki = 0;
  for (int m = 0; m <= 8; m++) {
    cas[m] = 0;
  }
  
  for (int i = 2; i <= 8; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, HIGH);
  }
  delayMicroseconds(10);

  for (int i = 2; i <= 8; i++)  
    pinMode(i, INPUT);
 
  while (kolki < 7) {
    kolki = 0;
    for ( int i = 2; i<=8; i++)
      if (digitalRead(i) == HIGH) {
        cas[i]++;
      }
      else kolki++;
  }
  
  if (cas[2] > CIARA) {
    lavy.write(120);
    pravy.write(120);
  } 
  else if (cas[8] > CIARA) {
    lavy.write(60);
    pravy.write(60);
  }
  else if (cas[7] > CIARA) {
    lavy.write(60);
    pravy.write(90);
  }
  else if (cas[3] > CIARA) {
    lavy.write(90);
    pravy.write(120);
  }
  else if (cas[6] > CIARA) {
    lavy.write(60);
    pravy.write(85);
  }
  else if (cas[5] > CIARA) {
    lavy.write(95);
    pravy.write(120);
  }
  else {
    lavy.write(60);
    pravy.write(120);
  }
}

Na robota sa pripojili ultrazvukový senzor na meranie vzdialenosti HC-SR04.

Ultrazvukový senzor na meranie vzdialenosti HC-SR04

V jeho datasheete sa dozvieme, akým spôsobom tento senzor komunikuje s jednočipovým mikropočítačom:

komunikácia so senzorom HC-SR04

Napíšeme jednoduchý program na meranie vzdialenosti pomocou tohto senzora: na pin TRIG vyšleme 10 usec pulz a potom zmeriame dĺžku pulzu, ktorou senzor odpovie v mikrosekundách. Nameraná vzdialenosť je počet_mikrosekúnd / 58.

// subor: ultrazvuk.ino
// zapojenie: D2: pin TRIG ultrazvukoveho senzora
//            D3: pin ECHO ultrazvukoveho senzora
// popis: program vypisuje nameranu vzdialenost na seriovy port 
#define TRIG 2
#define ECHO 3

void setup() {
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
  Serial.begin(115200);
}

void loop() {
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG, LOW);
  
  while (digitalRead(ECHO) == LOW) { }

  int cas = 0;
  while (digitalRead(ECHO) == HIGH) {
    cas ++;
    delayMicroseconds(1);
  }

  Serial.println(cas / 58);
  delay(200);
}

Tento program ale nemeria presne, pretože meranie okrem príkazu na čakania 1 usec program vykonáva aj iné inštrukcie, ktoré tiež trvajú nejaký čas. Niekedy neskôr si ukážeme lepší spôsob merania pomocou časovačov.

Týmto končí náša krátka exkurzia s robotmi Acrob. S Arduinami budeme ešte pokračovať, ale musíme si vytvoriť vlastný návrh robotov.