Arduino

Zo stránky Robotický krúžok
Verzia z 17:25, 6. január 2017, ktorú vytvoril Palo (diskusia | príspevky)
Skočit na navigaci Skočit na vyhledávání

Slovo Arduino označuje niekoľko vecí:

  • je to počítač (malá doska plošného spoja osadená väčšinou jednočipovým mikropočítačom Atmel AVR ATMega328)
  • je to programovací jazyk v ktorom sa tento počítač dá programovať
  • je to program - programovacie vývojové prostredie, pomocou ktorého sa program zapisuje a zároveň cez USB kábel prenáša a spúšťa na Arduine
  • sú to všetky tieto veci dokopy.

Arduino sa vyrába v rozličných verziách.

Arduino Uno
Arduino Uno - najrozšírenejší model Arduina

Arduino Nano
Arduino Nano - funguje rovnako ako Uno, ale je oveľa menšie - takéto budeme používať aj my

Robot Acrob
Robot Acrob - používa dosku, ktorá sa podobá na Arduino, ale funguje rovnako, mali sme ich požičané od združenia Robotika.SK na krúžku

ATMega328
Jednočipový mikropočítač ATMega328, ktorý je základom Arduina

ATMega328 pinout
Funkcie jednotlivých vývodov jednočipového mikropočítača ATMega328

Podrobné informácie o jednočipovom mikropočítači ATMega328 sa dajú nájsť v jeho datasheete.

Každý počítač 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:

 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:

 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:

 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:

 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:

 #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:

 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

 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.

 #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ť.

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:

 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

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):

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.

#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