Nauč se s námi víc. Využij 50% zdarma na e-learningové kurzy.
discount week 50

Lekce 17 - Tvorba vlastní Arduino knihovny - Třída

V minulé lekci, Arduino a analogový pin, jsme si řekli něco o analogovém pinu a jak jej lze využít pro měření elektrického odporu a napětí.

V dnešním Arduino tutoriálu si vyzkoušíme základy objektově orientovaného programování v praxi. Vytvoříme třídu pro blikání LED diodou. Třídu pak můžeme používat několikrát, vytvořením více instancí. Právě znovupoužívání kódu je jednou z výhod OOP. Začněme ale hezky popořadě.

Co je to OOP?

OOP neboli Objektově Orientované Programování v sobě nese základní myšlenku pohlížet na části programu jako na reálné objekty. S takovými objekty se pak daleko lépe pracuje, protože obvykle odpovídají např. objektům, které přes Arduino ovládáme (LED dioda, robotické rameno, ...).

Třídy

Objekty určitého typu tvoříme tak, že si prvně definujeme tzv. třídu. Ta dovoluje vytvořit objekt, který se vyznačuje svým specifickým chováním a obsahuje své vlastní atributy. Navíc má i svou ojedinělou identitu. Výhodu tohoto přístupu si ukažme na následujícím programu.

Blikačka bez třídy

Vytvoříme si funkci pro blikání se všemi 3 použitelnými LED diodami zapojenými na desce Arduina (RX, TX a L). Nebudeme tedy potřebovat žádné schéma zapojení ani nepájivé pole, postačí jen deska libovolného Arduina.

Kód programu je následující:

bool stav;
long posledniZmena;

void Blikej(int pin, int interval) {
  if ((millis() - posledniZmena) > interval) {
    stav = !stav;
    posledniZmena = millis();
  }
  digitalWrite(pin, stav);
}

void setup() {
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(13, OUTPUT);
}

void loop() {
  Blikej(0, 125);
  Blikej(1, 250);
  Blikej(13, 500);
}

Kód využívá funkci millis(), která se od funkce delay() (známou z předchozích lekcí) liší tím, že se jedná o čítač milisekund od spuštění programu, nikoliv o pauzu.

Program má blikat hned třemi LED diodami a to každou v jiném intervalu. Aby mohly být intervaly úplně různé, každá dioda si podle aktuálního času a posledního času změny stavu zjistí, zda se má nebo nemá změnit její stav.

Jak můžete vidět, diody na desce se chovají dosti nestabilně. Asi tušíte, že problém je v tom, jak je pro všechna volání této funkce vždy stejná proměnná long posledniZmena. Ostatní volaní této funkce jsou tedy zdržována. Takhle by to nešlo.

Problém bychom určitě mohli vyřešit i bez objektů, např. vytvořit samostatnou proměnnou pro každou ledku zvlášť nebo dát LED diody do pole. My si dnes ovšem ukážeme objektové řešení.

Blikačka se třídou

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Použitím třídy vytvoříme 3 samostatné objekty pro každou z LED diod, se svými vlastními proměnnými.

Třída

Nejprve si deklarujeme třídu Blikacka, která definuje co vše budou budoucí blikačky mít.

Třída je vzor, podle kterého pak lze vytvořit libovolné množství objektů, kterým říkáme instance třídy. Ty budou mít všechny ty samé proměnné a funkce jako definuje třída, ale každý objekt v nich pak bude mít své vlastní hodnoty.

Deklarace metod a atributů třídy

Kód třídy bude zatím následující:

class Blikacka {
  public:
    Blikacka(int pin);
    void Blikej(int interval);
    void Neblikej();

  private:
    int pin;
    bool stav = false;
    long posledniZmena ;
};

Za složeným blokem ohraničujícím třídu se skutečně píše středník!

Třídě jsme deklarovali několik funkcí a proměnných. Těm se nyní terminologicky správně již říká metody a atributy, protože patří daným instancím třídy, blikačkám, které podle třídy jako vzoru vytvoříme.

První metoda bez datového typu, Blikacka(), je tzv. konstruktor. Zavolá se automaticky hned po vytvoření konkrétní nové blikačky. Asi netřeba říkat, že pak slouží k inicializaci hodnoty atributů.

Kód metod třídy

Následně po deklaraci třídy doplníme jednotlivé metody a konstruktor prostřednictvím kvalifikátoru "čtyřtečky" :: (neboli operátoru příslušnosti).

Pod deklarací této třídy program pokračuje:

// konstruktor
Blikacka::Blikacka(int pin) {
  if (pin < 0 || pin > 13) pin = 13;
  this->pin = pin;
  pinMode(pin,OUTPUT); // díky tomuto již nepotřebujeme smyčku setup()
}

// metoda pro blikání
void Blikacka::Blikej(int interval) {
  if (interval <= 0) interval = 250;
  if ((millis() - posledniZmena ) > interval) {
    stav = !stav;
    posledniZmena = millis();
  }
  digitalWrite(pin,stav);
}

// metoda pro přerušení blikání
void Blikacka::Neblikej() {
  digitalWrite(pin,LOW);
}

// vytvoření tří instancí
Blikacka X(0);
Blikacka Y(1);
Blikacka Z(13);

void setup() {

}

void loop() {
  X.Blikej(125);
  Y.Blikej(250);
  Z.Blikej(500);
}

Metody jsou doplněny podmínkami pro omezení rozsahu užitých pinů Arduina (0 - 13, uvažujme základní Arduino Nano či Uno), a intervalu - interval 0 ms a menší je nesmysl.

Zde si všimněme několika dalších (pro nás zatím nových) výrazů.

this a operátor ->

Jako první je výraz this->pin = pin. Tímto říkáme programu, že do atributu instance třídy s názvem pin (v privátní části) přiřadíme hodnotu parametru pin z konstruktoru Blikacka(int pin). Právě pomocí this odlišíme která proměnná pin je která. this je ukazatel na instanci samotnou, proto vše označené tímto ukazatelem je vždy součástí instance.

U Arduino programů se běžně užívají pro odlišení duplicitních názvů podtržítka. V tomto případě by tedy tento atribut pak měl tvar _pin. Tato praktika je sice běžná, avšak více se upřednostňuje varianta s ukazatelem this, která je (i podle mého názoru) přehlednější. A vzhledem k tomu, že jde o ukazatel, má i tzv. ukazatelový selektor (->). Se selektorem se setkáváme běžně, avšak jako s operátorem tečka (například při tisku na sériový monitor jsme používali metodu Serial.println()). Případné zájemce o více informací ohledně ukazatele this odkážeme na lekci v programovacím jazyce C++.

Modifikátory přístupu

Dalším výrazům public a private říkáme modifikátory přístupu (někdy též přístupové úrovně) a v podstatě nám říkají, s čím můžeme mimo třídu pracovat, a co je skryto.

Modifikátorem public označujeme vše, s čím chceme, aby nám třída dovolila nějakým způsobem veřejně manipulovat, například volat její metody. K takovýmto částem třídy pak přistupujeme pomocí selektoru tečky. Například pomocí X.Blikej() voláme veřejnou metodu Blikej() instance X. Atributy a metody označené modifikátorem public nazýváme jako veřejné.

Je dobrou praktikou označovat všechny atributy jako soukromé a jejich hodnoty měnit pouze prostřednictvím veřejných metod! Naopak modifikátor přístupu private označuje vše, co je skryté, a my k tomu tedy nemáme přístup. Tímto modifikátorem se nejčastěji označují ty atributy a metody, které třída využívá ke své vlastní činnosti. Například jde o operace, do kterých není vhodné nějakým způsobem zasahovat. O částech třídy označené modifikátorem private vždy mluvíme jako o soukromých částech třídy. K takovýmto atributům nelze veřejně přistupovat prostřednictvím selektoru. Jejich hodnoty lze ale měnit jen ve veřejných metodách. My hodnotu privátního atributu v tomto případě nastavujeme v konstruktoru, jedná se právě o privátní atribut pin.

Schválně si můžeme vyzkoušet vypsat hodnotu atributu posledniZmena (Serial.println(X.posledniZmena)). Kompilátor nám nyní vynadá. Když ale atribut označíme jako veřejný, při každé iteraci smyčky loop() nám bude vypsána hodnota v něm uložená.

Kromě modifikátorů přístupu public a private existuje ještě modifikátor protected, který je podobný privátnímu modifikátoru, avšak jej lze dědit v odvozených třídách. To je ovšem nad rámec této lekce.

Celý program

Tak, dost vysvětlování, jdeme na praktickou ukázku! Nyní nahrajme již ucelený program do Arduina!

class Blikacka {
  public:
    Blikacka(int pin);
    void Blikej(int interval);
    void Neblikej();

  private:
    int pin;
    bool stav = false;
    long posledniZmena ;
};

// konstruktor
Blikacka::Blikacka(int pin) {
  if (pin < 0 || pin > 13) pin = 13;
  this->pin = pin;
  pinMode(pin,OUTPUT);
}

// metoda pro blikání
void Blikacka::Blikej(int interval) {
  if (interval<= 0) interval = 250;
  if ((millis() - posledniZmena ) > interval) {
    stav = !stav;
    posledniZmena = millis();
  }
  digitalWrite(pin,stav);
}

// metoda pro přerušení blikání
void Blikacka::Neblikej() {
  digitalWrite(pin,LOW);
}

// vytvoření tří instancí
Blikacka X(0);
Blikacka Y(1);
Blikacka Z(13);

void setup() {

}

void loop() {
  X.Blikej(125);
  Y.Blikej(250);
  Z.Blikej(500);
}

To už je lepší, nemyslíte? Třída Blikacka je k dispozici ke stažení níže.

V příští lekci, Tvorba vlastní Arduino knihovny - Dokončení, si vytvoříme naši plnohodnotnou Arduino knihovnu.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 20x (732 B)
Aplikace je včetně zdrojových kódů

 

Předchozí článek
Arduino a analogový pin
Všechny články v sekci
Arduino
Článek pro vás napsal Václav Doškář
Avatar
Jak se ti líbí článek?
1 hlasů
Autor pracuje jako pedagog v oblasti elektroniky a elektrotechniky. Programování má jako koníčka. Věnuje se jazykům C++ a C#.
Aktivity

 

 

Komentáře

Avatar
Martin Jína
Člen
Avatar
Martin Jína:30. května 10:26

Ahoj, tak takhle to nepůjde!
To je zamotaný jak špagety na talíři! Prosím trochu více vysvětlování a nepoužívat stejná slova pro více atributů nebo čeho že to?
Díky

 
Odpovědět
30. května 10:26
Avatar
Václav Doškář
Člen IT Redactor Gang
Avatar
Odpovídá na Martin Jína
Václav Doškář:3. června 8:38

Ahoj, děkuji za reakci!

Které části konkrétně nerozumíš? Jako první je uveden příklad, jak by to být nemělo, následuje deklarace hlavičky třídy po které následuje deklarace jednotlivých metod. V závěru je pak již kompletní kód (který stačí jen zkopírovat a vložit do Arduino IDE).

Klidně mi sem vpal konkrétní případy, které tě matou ;)

Odpovědět
3. června 8:38
Čím blbovzdornější program napíšete, tím efektivnější bude!
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Martin Jína
Člen
Avatar
Martin Jína:3. června 10:25

To strukturované programování mi nečiní problém a vyznám se v tom.
OOP - jsem naprosto nepochopil. Mám návyky ještě z assembleru a tohle jde proti všemu, co jsem kdy dělal.
Jsem rád, že se snažíš pomoci nám, kteří to nechápou. Vím, že tento příklad je jednoduchý, ale pro mne je to prostě Španělská vesnice. Byl bych ti strašně vděčný, kdyby jsi použij ještě jednodušší příklad, třeba něčeho co by v praxi pro jednoduchost neobstálo, a na tom vše vysvětlit. Prosím o výklad každého zhluku znaků, které v kódu použiješ (každého slova). To co tobě připadá naprosto samozřejmé a přirozené, může být pro jiné kámen úrazu a velmi zavádějící. Neboj se použít mnoho slov a vysvětlovat to, co jsi již vysvětlil. Raději několikrát, než vůbec.

Předem děkuji Martin.

 
Odpovědět
3. června 10:25
Avatar
Václav Doškář
Člen IT Redactor Gang
Avatar
Odpovídá na Martin Jína
Václav Doškář:3. června 15:54

Prosím o výklad každého zhluku znaků, které v kódu použiješ (každého slova)

Ale pokud nerozumíš základům, proč nezačneš jednoduššími lekcemi? Bez těch základních znalostí se stejně z místa nehneš. OOP je detailně vysvětleno zde, prostudoval jsi tyto lekce? Tím tě nechci nijak odrazovat, naopak! Kolega tyto lekce vytvořil právě pro ty, co chtějí OOP v C++ pochopit :)

Odpovědět
3. června 15:54
Čím blbovzdornější program napíšete, tím efektivnější bude!
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.