Píšeme knihovnu pro Arduino

Vytváříme knihovny pro rozšíření funkcí desky Arduino. Návod provází čtenáře krok za krokem převodem běžného programu na knihovnu.

Tento dokument vysvětluje, jak vytvořit knihovnu pro Arduino. Začíná sketchem pro blikání Morseovky a vysvětluje, jak převést funkce tohoto programu do knihovny. To umožní jiným uživatelům použít kód, který jste napsali a jednoduše jej aktualizovat, když jej vylepšíte. Nebo také zpřehlednit váš program a rozčlenit jej do logických celků.

Článek je volným překladem článku Writing a Library for Arduino na webu Arduino. Pro více informací si přečtěte API Style Guide pro zásady vytváření knihoven.

Běžný program

Začneme běžným programem (sketch), který zabliká na výstup kód v „morseovce“:

int pin = 13;

void setup()
{
  pinMode(pin, OUTPUT);
}

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

void dot()
{
  digitalWrite(pin, HIGH);
  delay(250);
  digitalWrite(pin, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(pin, HIGH);
  delay(1000);
  digitalWrite(pin, LOW);
  delay(250);
}

Když program spustíte, zabliká signál SOS (nouzové volání) na pinu 13.

Program má několik částí, které potřebujeme převést do knihovny. Jsou to dvě funkce dot() a dash(). Dále tu máme proměnnou pin, která nám určuje pin pro výstup. A také tu voláme funkci pinMode(), která inicializuje pin pro výstup.

Převádíme program na knihovnu

K vytvoření knihovny potřebujeme dva soubory:

  1. Header file, neboli hlavičkový soubor (soubor s příponou .h)
  2. Source file, neboli zdrojový soubor (soubor s příponou .cpp)

Hlavičkový soubor obsahuje definici knihovny. Obvykle seznam všeho, co knihovna obsahuje (konstanty, proměnné, třídy, metody). Zdrojový kód potom obsahuje vlastní kód jednotlivých funkcí/metod.

Protože naše knihovna umožňuje posílat na výstup Morseovy kódy, nazveme knihovnu Morse.h. Pojďme se podívat, co bude uvnitř. Napoprvé to může vypadat podivně nebo složitě. Ale když to uvidíte a promyslíte, začne to dávat smysl.

Hlavičkový soubor Morse.h

Základem každého hlavičkového souboru jsou jednotlivé řádky kódu, vložené do třídy:

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

Třída je, zjednodušeně řečeno, kolekce funkcí a proměnných, které jsou pohromadě na jednom místě, patří jednomu (logickému) celku. Tyto funkce/metody a proměnné mohou být:

  • Public – mohou k nim přistupovat uživatelé, které používají knihovnu, jsou tzv. věřejné
  • Private – lze k nim přistupovat pouze ze samé knihovny, jsou privátní. Tomu se říká princip zapouzdření (encapsulation). Nikdo tak nemůže měnit vnitřní záležitosti třídy. K tomu se používají funkce (settery a gettery).

Každá třída má také speciální funkci, která se volá při vytváření instance třídy, tj. objektu (třída je popis, objekt je živá instance třídy, objekt vytvořený na základě popisu třídy). Této speciální funkci se říká konstruktor. Konstruktor má stejný název jako třída samotná a nemá žádnou návratovou hodnotu (i přesto se před název funkce nepíše klíčové slovo void).

Do hlavičkového souboru patří hromada dalších věcí. Jednou z nich je direktiva #include, která přidává přístup základním typům a konstantám Arduina. Vypadá přesně takhle (a patří nad výše uvednou definici třídy):

#include "Arduino.h"

Dále je běžné obalit obsah celého hlavičkového souboru do zvláštně vypadající konstrukce:

#ifndef Morse_h
#define Morse_h

// direktiva #include a dalsi kod patri sem...

#endif

Tato konstrukce zabraňuje tomu, aby někdo nechtěně nenaincludoval knihovnu vícekrát. Těmto direktivám se říká header guards, tedy ochránci headeru.

Je dobrým zvykem na začátek hlavičkového souboru vložit komentář s názvem knihovny, krátkým popisem co knihovna dělá, kdo ji napsal, datum a licenci.

Pojďme se tedy podívat na celý hlavičkový soubor:

/*
  Morse.h - Library for flashing Morse code.
  Created by David A. Mellis, November 2, 2007.
  Released into the public domain.
*/
#ifndef Morse_h
#define Morse_h

#include "Arduino.h"

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

#endif

Zdrojový kód Morse.cpp

A teď se pojďme podívat na jednotlivé části zdrojového souboru (source file) Morse.cpp. Nejprve přidáme dvě direktivy #include. První přidá přístup k běžným Arduino funkcím a druhá k našemu hlavičkovému souboru:

#include "Arduino.h"
#include "Morse.h"

Pak přichází na řadu konstruktor. To je funkce, která se zavolá při vytvoření instance třídy. Třída je popis, když vytvoříme instanci třídy, tj. vytvoříme nový objekt, zavolá se konstruktor. To je funkce, která se používá k nastavení objektu, např. pro inicializaci různých proměnných. My nastavíme pin pro výstup (OUTPUT) a uložíme číslo pinu do proměnné _pin pro pozdější použití funkcemi třídy Morse:

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

V tomto kódu je určitě několik zvláštností:

  • Morse:: před jménem funkce. To nám říká, že tato funkce je součástí/prvkem třídy Morse. Uvidíte to i u dalších funkcí ve třídě Morse.
  • Podtržítko před názvem proměnné. Tato proměnná může mít libovolný název. Je však zvykem privátní proměnné označovat podtržítkem. Také nám to umožňuje dobře rozlišit mezi privátní proměnou a argumentem funkce (v tomto případě pin).

A nyní již přichází na řadu vlastní kód z původního programu. Vypadá vlastně skoro stejně, kromě přidání Morse:: na začátek funkcí a _pin místo pin:

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

Opět je dobrým zvykem přidat na začátek zdrojového souboru komentář. Podívejme se tedy na kompletní zdrojový soubor:

/*
  Morse.cpp - Library for flashing Morse code.
  Created by David A. Mellis, November 2, 2007.
  Released into the public domain.
*/

#include "Arduino.h"
#include "Morse.h"

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

Používáme vytvořenou knihovnu

A nyní použijeme původní program, ale s naší novou knihovnou:

#include "Morse.h"

Morse morse(13);

void setup()
{
}

void loop()
{
  morse.dot(); morse.dot(); morse.dot();
  morse.dash(); morse.dash(); morse.dash();
  morse.dot(); morse.dot(); morse.dot();
  delay(3000);
}

Na začátku programu vidíme direktivu #include, která připojí do našeho programu knihovnu Morse. Dále vidíme, jak se vytvoří instance třídy Morse jako objekt morse. Jediný argument konstruktoru předává číslo pinu (13) pro výstup.

Funkce setup() je nyní prázdná, protože pin pro výstup se již nastavil v konstruktoru objektu morse.

U funkcí dot() a dash() musíme přidat prefix s názvem objektu, ke kterému metody patří, tedy morse.dot(), morse.dash().

Dále můžeme vytvářet další instance objektu Morse (s jiným pinem) a volat jejich metody:

Morse morse(13);
Morse morse2(12);

morse.dot();
morse2.dash();

Napsat komentář