Low Power

low power, polling, battery life, interrupts

The Problem of Power Consumption in IoT

(slide) Internet vecí so svojím príchodom priniesol niekoľko zaujímavých tém, ktoré je potrebné riešiť. Patria medzi ne témy ako je bezpečnosť, ergonómia, technológie pre komunikáciu, ale hlavne nízka spotreba zariadení. Zariadenia IoT sú totiž častokrát napájané batériou, pretože nemajú priamy prístup k napájaniu. To je častokrát spôsobené tým, že sú umiestnené na miestach, kde prístup k elektrickej sieti proste nie je možný.

(slide) Hľadanie spôsobov, ako zabezpečiť nízku spotrebu, však samozrejme so sebou neprinieslo až IoT. Už dávno predtým sme tu mali kalkulačky, diaľkové ovládače, digitálne hry, ale aj notebooky alebo mobilné telefóny. Všetky tieto zariadenia boli napájané z batérií. IoT zariadenia však predstavujú špeciálnu skupinu zariadení, kedy sa očakáva, že tieto zariadenia budú schopné vykonávať svoju činnosť bez zásahu používateľa mesiace až roky. A pred vyčerpaním batérie ešte stihnú s dostatočným predstihom upozorniť, že je potrebné batériu vymeniť. Energeticky autonómne zariadenia sú totiž základom IoT.

V prípade IoT zariadení si však nemôžeme dovoliť to isté, čo v prípade štandardných zariadení napájaných z elektrickej siete a teda - premieňať prebytok energie na teplo.

(slide) So zvyšujúcim sa množstvom prenosných zariadení vznikajú stále nové a nové technológie pre zvyšovanie kapacity batérií. Rovnako tak vznikajú aj postupy na to, ako zabezpečiť nízky odber energie. Takýchto postupov je niekoľko, ale v princípe je možné ich rozdeliť do dvoch základných skupín a síce:

  1. postupy pre znížovanie odberu pri navrhovaní softvéru, a
  2. postupy pre znížovanie odberu pri navrhovaní hardvéru.

IoT zariadenia totiž častokrát nevyžadujú pre svoju činnosť, aby boli neustále napájané. Napríklad pri meraní teploty v miestnosti nie je potrebné, aby jej odčítanie zo senzora teploty prebehlo každú sekundu. V tomto prípade si je možné vystačiť dokonca s niekoľko minútovým intervalom merania teploty. Kým tento interval neuplynie, tak zariadenie nerobí nič. Nič však môže znamenať uspatie procesu na potrebnú dobu pomocou volania funkcie delay() (v prípade napr. prototypovacej dosky Arduino Uno). To sa síce môže javiť tak, že mikrokontrolér nič nerobí, pretože funkcia delay() je blokovacia, ale zariadenie bude elektrickú energiu odoberať aj naďalej. Ak sa však na potrebný čas uvedie do režimu spánku, jeho odber elektrickej energie bude minimálny.

(slide) Uviesť mikrokontrolér do režimu spánku však nie je jediný spôsob, ktorým je možné znížiť odber elektrickej energie. Martin Malý vo svojej knihe Hradla, volty, jednočipy uvádza aj ďalšie postupy:

  • zníženie napájacieho napätia (aspoň na úroveň 3,3V)
  • spomaliť prácu mikrokontroléra (napr. jeho uspaním alebo znížením jeho pracovnej frekvencie)
  • používať CMOS technológiu, ktorá má v kľude veľmi nízky odber

Znížiť odber napájania je samozrejme možné aj výberom vhodných komponentov. Častokrát môže ísť o zbytočné LED diódy, ktoré síce v porovnaní s inými zdrojmi svetla nízky odber, ale pri neustálom svietení dokážu spoľahlivo a rýchlo vybiť zdroj napájania. V prípade IoT zariadení sú však zaujimáve komponenty zabezpečujúce komunikáciu. Existujú totiž technológie, ktoré sú absolútne nevhodné pre tento typ komunikácie, zatiaľ čo iné boli dizajnované práve pre tento typ zariadení.

Pri navrhovaní hardvéru si je tiež dobré uvedomiť, že celkovú spotrebu ovplyvňuje spotreba jednotlivých komponentov. Niektoré náročnejšie senzory dokonca krátkodobo vyžadujú veľmi veľký prúd aj napriek tomu, že počas bežnej prevádzky, resp. pri uspaní je ich spotreba minimálna. Po zobudení alebo pri pripájaní (napr. niektoré komunikačné moduly) sú však schopné krátkodobo vyžadovať odber aj 2A. Batéria teda musí byť pripravená v závislosti od komponentov IoT zariadenia aj na takúto krátkodobú záťaž.

Battery Life

(slide) Pri vývoji nových zariadení pre internet vecí je vždy dôležitou otázkou, ako dlho vydrží zariadenie pracovať, keď je napájané z batérie. Odpoveď na túto otázku záleží od dvoch vecí:

  • od kapacity batérie, a
  • od spotreby zariadenia.

Battery Capacity

(slide) Kapacita batérie sa udáva v jednotke Ah (ampérhodina), resp. v nižších jednotkách mAh (miliampérhodina). Batéria má kapacitu jednej ampérhodiny, ak je schopná pri svojom nominálnom napätí dodať teoreticky do záťaže prúd 1A po dobu 1 hodiny.

Kapacita batérií rovnakého typu sa môže líšiť v závislosti od výrobcu. Prehľad kapacít najznámejších batérií na trhu je zobrazený v nasledujúcej tabuľke. Batérie sú označené podľa ANSI označenia. Podľa IEC označenia sú označené len mincové a gombíkové batérie.

Typ Označenie Kapacita Napätie
mincová batéria CR2032 190 - 225 mAh 3 V
gombíková batéria R44 15 - 600 mAh 3 V
tužková batéria AA 600 - 3400 mAh 1.2 - 1.5 V
mikrotužková batéria AAA 350 - 1250 mAh 1.25 - 1.5 V
9V batéria 9V 400 - 1200 mAh 7.2 - 9 V

(slide) Pri dizajnovaní a prevádzkovaní IoT zariadení sa neodporúča uvažovať o nabíjacích bateriách. Nabíjateľné batérie sú do zariadení, ktoré sa používajú denne, ako napr. MP3 prehrávače, fotoaparáty alebo klávesnice a myši. Použitie nabíjateľných batérií je v tomto prípade finančne výhodnejšie, ako dokupovať neustále nové batérie.

IoT zariadenia sú totiž zariadenia, ktoré sa nepoužívajú denne, resp. neustále. Častokrát sa takéto zariadenie len zobudí, odčíta hodnotu zo senzora, odošle ju na spracovanie a opäť zaspí. Nabíjateľné batérie totiž trpia samovoľným vybíjaním, ktoré znižuje ich výdrž. Aj bez toho, aby bola nabíjateľná batéria používaná, sa môže po pár mesiacoch sama vybiť. Použitím nenabíjateľných (teda alkalických) batérií sa je možné tomuto problému vyhnúť. Zariadenia napájané alkalickými batériami dokážu fungovať mesiace až roky bez nutnosti výmeny batérií.

Device Consumption

(slide) Určiť spotrebu zariadenia je o niečo zložitejšie. Zariadenie totiž môže počas svojej prevádzky v závislosti od ďalších pripojených komponentov (obvodov) odoberať v rozličnom čase rozličné množstvo energie.

Získať dobu životnosti batérie, resp. čas, počas ktorého je možné zariadenie napájať z batérie, je možné ako podiel kapacity batérie a veľkosti prúdu pretekajúceho záťažou. Ak by sme teda pripojili Arduino Uno, ktoré po zapojení odoberá prúd okolo 50mA k batérii, ktorá má kapacitu 1000mAh, vydrží byť Arduino napájané spolu 20h:

\[ t = \frac{BatteryCapacity}{LoadCurrent} = \frac{1000 mAh}{50 mA} = 20 h\]

Managing Energy Consumption with Software

(slide)

Motivation in the Beginning

(slide) Predstavte si, že pracujete na niečom veľmi dôležitom. Ste totálne sústredený, keď vtom sa náhle ozve váš žalúdok a dá vám rozličnými akusticko-citlivými možnosťami najavo, že máte hlad. Neváhate, však to predsa nie je prvýkrát, keď ste vyhladli, a vytočíte svoju obľúbenú pizzeriu, objednáte si svoju obľúbenú pizzu a spojovateľka vám oznámi, že vám vašu pizzu doručia až o 60 minút. Takže ešte 60 minút budete zápasiť s hladom.

Čo teraz? Do úvahy prichádza niekoľko možností:

  1. nebudem robiť ďalej, pokiaľ sa nenajem - takže nasledujúcich 60 minút strávim sledovaním hodiniek
  2. budem ďalej robiť hladný a popri tom budem pravidelne sledovať hodinky, či už neuplynulo tých 60 minút
  3. nastavím si budik, aby ma po uplynutí 60 minút upozornil a ja som teda nemusel sledovať neustále hodinky, ale môžem sa plne sústrediť na prácu

Aj napriek tomu, že tento príklad z reálneho života vyznieva vzhľadom na uvedené možnosti pomerne komicky, môžeme isté paralely nájsť v mnohých našich, ale aj v rozličných existujúcich IoT riešeniach. Takže - čo robíme zle?

The Polling

(slide) Pamätáte sa na druhú časť animáku Shrek? Shrek šiel spolu s Fionou a oslíkom na návštevu k Fioniným rodičom do kráľovstva za siedmymi horami a siedmymi dolinami. A pamätáte sa na to, čo robil celý čas oslík? Celý čas sa pýtal rovnakú otázku: “Kedy tam už budeme?”

A presne rovnaký prístup častokrát volíme pri čítaní hodnôt zo senzorov - každé volanie funkcie loop() sa začne prečítaním hodnoty výstupu zo senzora. A to celé sa udeje niekoľkokrát za sekundu. Aby sa dopytovanie nedialo príliš často, funkciu je možné na krátky čas uspať. Tým je zabezpečené, aby sa dopytovanie na výstup z PIR senzoru vykonalo práve 2x za sekundu.

Pri takomto prístupe sa správame presne tak, ako oslík v Shrekovi - neustále sa pýtame na tú istú vec. Znova a znova. Vo svete informatiky sa tento prístup nazýva slovom polling, pretože sa pýtame (z angl. poll). Jeho princíp spočíva v neustálom dopytovaní sa externého zariadenia, či na ňom nedošlo k požadovanej udalosti, resp. k zmene stavu. Polling predstavuje synchrónnu operáciu.

(slide) Polling je veľmi jednoduchý a častokrát postačuje pre riešenie väčšiny problémov. Má však aj niektoré nevýhody. Jednou z nich je vysoká pravdepodobnosť straty údajov. K tomu môže dôjsť napr. vtedy, ak externé zariadenie pošle údaje mikrokontroléru tesne potom, ako sa ich ten pokúsil prečítať. Alebo aj vtedy, ak dôjde k pohybu uprostred 500 ms prestávky a počas tých istých 500 ms sa pohyb aj skončí.

V ilustrácii na začiatku kapitoly o doručení pizze môže dôjsť k problému napr. vtedy, ak bude pizza doručená pred uplynutím 60 min intervalu. Obecne takýto prístup nepredstavuje veľmi dobrý dizajn, ak je potrebné zákazníkovi povedať, že pizza dorazí presne po 60 minútach a ak vtedy neotvorí, svoj tovar nedostane (aj napriek tomu, že zaň zaplatil).

Samozrejme existujú požiadavky, pri ktorých je takéto správanie postačujúce. Napr. merať vonkajšiu teplotu stačí v niekoľkominútových pravidelných intervaloch a prípadný výkyv teplôt medzi dvoma meraniami je možné považovať za zanedbateľný.

Vďaka neustálemu dopytovaniu sa si polling vyžaduje vyhradenie väčšieho množstva zdrojov a hlavne času mikrokontroléra. Ten teda miesto vykonávania potrebných operácií plytvá svojimi zdrojmi na neustále kontrolovanie stavu externého zariadenia. To sa priamoúmerne dokáže prejaviť na spotrebe energie, pretože čím častejšie kontrolujeme stav externého zariadenia, tým viac energie spotrebujeme.

The Interrupts

Riešenie pôvodného problému s doručením pizze je však veľmi jednoduché - keď doručovateľ s pizzou dorazí, zazvoní alebo zaklope na dvere zákazníka. Poprípade ho pred príchodom upozorní telefonátom. Ten sa teda nemusí starať o to, či uplynula doba doručenia alebo či náhodou neprišiel skôr. Môže sa venovať svojim dôležitejším povinnostiam a na vznik udalosti doručenia bude automaticky a hlavne včas upozornený.

(slide Tento mechanizmus sa v svete informačných technológií nazýva prerušenie (z angl. interrupt). Koncept prerušení je vo svete mikrokontrolérov veľmi dôležitý.

Prerušenie je mechanizmus, ktorým dá prostredie mikrokontroléru vedieť, že sa práve stalo niečo dôležité. Vo všeobecnosti je možné povedať, že prerušenie je špeciálny signál pre mikrokontrolér, ktorý poslal softvér alebo hardvér a vyžaduje si okamžitú pozornosť.

Výhodou takéhoto prístupu je vo všeobecnosti zníženie zaťaženia mikrokontroléra, ktorý nemusí stále dookola testovať, či k udalosti došlo alebo ešte nie (viď polling). V porovnaní s polling-om má prerušenie lepší reakčný čas, pretože reaguje na udalosť okamžite.

Princíp prerušenia sa využíva aj v architektúre riadenej udalosťami (a zngl. event driven architecture) alebo v programovaní riadenom udalosťami (z angl. event driven programming). V objektovom programovaní je zasa tento prístup opísaný pomocou návrhového vzoru pozorovateľ (z angl. observer).

Podobne, ako keď donášková služba volá priamo zákazníkovi, aby ho informovala o tom, že je donáška pripravená, aj požiadavku o prerušenie dostane od zariadenia priamo mikrokontrolér Tým pádom môže prísť požiadavka o prerušenie kedykoľvek a prerušenie predstavuje asynchrónny spôsob komunikácie.

Hlavný rozdiel medzi polling-om a prerušením je v tom, či sa softvér sám opýta alebo hardvér sám oznámi, či došlo k predmetnej udalosti.

Interrupt Service Routine

(slide) Inštrukcie programu, ktorý beží v mikrokontroléri, sa (obyčajne) vykonávajú sekvenčne. To znamená, že po vykonaní jednej inštrukcie sa vykoná nasledujúca inštrukcia v poradí. Akonáhle však mikrokontrolér dostane požiadavku na prerušenie, pozastaví sa vykonávanie inštrukcií programu mikrokontroléra a ten spustí špeciálnu funkciu na spracovanie prerušenia, ktorá sa označuje ISR (z angli. Interrupt Service Routine). Po jej skončení sa obnoví vykonávanie prerušeného programu vykonaním ďalšej inštrukcie v poradí.

Calling of ISR

(slide) ISR funkcie nevracajú žiadnu hodnotu a nemajú žiadny parameter. Ak je teda potrebné vo vnútri ISR funkcie zmeniť stav správania aplikácie, je na to možné použiť globálne premenné. Je to vlastne jediný spôsob, pomocou ktorého je možné prenášať údaje medzi ISR a hlavným programom.

(slide) Pri tvorbe ISR je dobré dodržiavať niekoľko odporúčaní:

  • ISR majú byť čo najkratšie a čo najrýchlejšie, aby zbytočne nebrzdili hlavný program alebo prípadne ďalšie prerušenia, ktoré môžu nastať.
  • Je potrebné sa vo vnútri ISR vyhnúť použitiu funkcie delay()!
  • Nepoužívať sériovú komunikáciu!
  • Ak programujete v jazyku C, tak globálne premenné, ktoré používate na zdieľanie údajov medzi ISR a programom, označte pomocou kvalifikátora volatile! Tým poviete prekladaču, že táto premenná môže byť použiteľná kdekoľvek v kóde a prekladač jej obsah vždy pri použití znovu načíta a nebude sa spoliehať na jej kópiu v registri. Zabránite tak aj prípadným optimalizáciám prekladača, vďaka ktorým by ju mohol napr. vyhodiť, pretože sa nepoužíva (v hlavnom programe).
  • Nevypínať ani nezapínať podporu prerušení vo vnútri ISR. V tomto prípade však existujú výnimky, ktoré budú opísané neskôr v kapitole.

Reasons to use Interrupts

(slide) Existuje veľa dôvodov, prečo prerušenia používať . Niektoré z nich sú tieto:

  • To detect pin changes (eg. rotary encoders, button presses)
  • Watchdog timer (eg. if nothing happens after 8 seconds, interrupt me)
  • Timer interrupts - used for comparing/overflowing timers
  • SPI data transfers
  • I2C data transfers
  • USART data transfers
  • ADC conversions (analog to digital)
  • EEPROM ready for use
  • Flash memory ready

Types of Interrupts

(slide) Pri práci s mikrokontrolérmi je možné prerušenia rozdeliť do dvoch skupín:

  1. hardvérové prerušenia, známe tiež ako externé prerušenia, alebo tiež pin-change prerušenia, a
  2. softvérové prerušenia, ktoré sú známe ako interné prerušenia alebo tiež časovače.

Ako už názov napovedá, signál prerušenia prichádza v prípade hardvérových alebo externých prerušení z externého zariadenia. Toto zariadenie je s mikrokontrolérom priamo prepojené. Keďže sú prerušenia asynchrónne, k prerušeniu môže dôjsť kedykoľvek.

Interné prerušenia zasa referujú na čokoľvek vo vnútri mikrokontroléra, čo dokáže vyvolať prerušenie. Príkladom môžu byť napríklad časovače, pomocou ktorých je možné zabezpečiť, aby k vyvolaniu prerušenia dochádzalo pravidelne napr. každú 1 sekundu.

Interrupt Vectors in ATmega328P

(slide) V jednej chvíli môžu byť naraz vyvolané viaceré žiadosti o prerušenie a mikrokontrolér sa musí rozhodnúť, ktorá z nich bude ošetrená ako prvá. Je teda potrebné, aby mikrokontrolér vedel povedať, ktoré prerušenia majú prednosť pred inými.

Mikrokontrolér obsahuje tzv. tabuľku vektorov prerušení (viď. tabuľka XXX). Táto tabuľka sa nachádza na začiatku programovej flash pamäti a obsahuje adresy ISR funkcií jednotlivých prerušení. V ich poradí je však aj priorita - čím má prerušenie nižšiu adresu, resp. vektor prerušenia má nižšie číslo, tým má vyššiu prioritu. Z tabuľky je teda možné vidieť, že najvyššiu prioritu má prerušenie od zdroja RESET a najnižšiu prioritu od zdroja SPM READY.

Vector No. Program Address Source Interrupt Definition
1 0x0000 RESET External Pin, Power-on Reset, Brown-out Reset and Watchdog System Reset
2 0x0002 INT0 External Interrupt Request 0
3 0x0004 INT1 External Interrupt Request 1
4 0x0006 PCINT0 Pin Change Interrupt Request 0
5 0x0008 PCINT1 Pin Change Interrupt Request 1
6 0x000A PCINT2 Pin Change Interrupt Request 2
7 0x000C WDT Watchdog Time-out Interrupt
8 0x000E TIMER2 COMPA Timer/Counter2 Compare Match A
9 0x0010 TIMER2 COMPB Timer/Counter2 Compare Match B
10 0x0012 TIMER2 OVF Timer/Counter2 Overflow
11 0x0014 TIMER1 CAPT Timer/Counter1 Capture Event
12 0x0016 TIMER1 COMPA Timer/Counter1 Compare Match A
13 0x0018 TIMER1 COMPB Timer/Coutner1 Compare Match B
14 0x001A TIMER1 OVF Timer/Counter1 Overflow
15 0x001C TIMER0 COMPA Timer/Counter0 Compare Match A
16 0x001E TIMER0 COMPB Timer/Counter0 Compare Match B
17 0x0020 TIMER0 OVF Timer/Counter0 Overflow
18 0x0022 SPI, STC SPI Serial Transfer Complete
19 0x0024 USART, RX USART Rx Complete
20 0x0026 USART, UDRE USART, Data Register Empty
21 0x0028 USART, TX USART, Tx Complete
22 0x002A ADC ADC Conversion Complete
23 0x002C EE READY EEPROM Ready
24 0x002E ANALOG COMP Analog Comparator
25 0x0030 TWI 2-wire Serial Interface
26 0x0032 SPM READY Store Program Memory Ready
: Reset and Interrupt Vectors in ATmega328 and ATmega328P

Handling the External Interrupts with Arduino

Vrátime sa teda k predchádzajúcemu zdrojovému kódu, kedy sme na detekciu pohybu využili metódu polling-u. Tentokrát sa problém pokúsime vyriešiť pomocou prerušenia. Konkrétne sa bude jednať o externé prerušenie, keďže prerušenie vyvolá PIR senzor tým, že pošle signál do mikrokontroléra vtedy, keď bude detekovať pohyb.

Ošetrenie prerušenia sa inicializuje pomocou funkcie attachInterrupt(). Inicializácia môže prebehnúť kdekoľvek v programe, ale pokiaľ sa ošetrenie prerušenia v programe nemení, je ideálne ho inicializovať vo funkcii setup(). Táto funkcia má tri parametre, ktoré hovoria o tom, čo všetko je potrebné vedieť pri ošetrovaní prerušenia:

  1. číslo prerušenia, ktoré je potrebné ošetriť,
  2. názov (resp. adresu) ISR funkcie, ktorá sa zavolá na jeho ošetrenie, a
  3. režim prerušenia, ktorý definuje, aké správanie na pin-e vyvolá prerušenie.

Pozrime sa najprv na to, aké správanie na pin-e vyvolá prerušenie. Prototypovacia doska Arduino UNO pozná tieto štyri režimy prerušenia:

  1. LOW - Prerušenie je vyvolané vtedy, keď sa na pine nachádza úroveň LOW. Tento proces sa deje napríklad vtedy, ak je tlačidlo pripojené k digitálnemu pinu v režime INPUT_PULLUP a po jeho stlačení sa na pin privedie úroveň LOW.

  2. CHANGE - Prerušenie je vyvolané vtedy, keď dôjde k zmene úrovne pinu buď z HIGH na LOW alebo z LOW na HIGH. Tento proces sa deje napríklad pri stláčaní prepínača.

  3. RISING - Prerušenie je vyvolané vtedy, keď dôjde k zmene úrovne z LOW na HIGH. Tento proces sa deje napríklad pri stlačení tlačidla.

  4. FALLING - Prerušenie je vyvolané vtedy, keď dôjde k zmene úrovne z HIGH na LOW. Tento proces sa deje napríklad pri uvoľnení stlačeného tlačidla.

Princíp jednotlivých režimov je ilustrovaný na nasledujúcom obrázku:

Iterrupt Mode

Na základe uvedených režimov prerušenia mikrokontroléra sa je možné stretnúť ešte s nasledujúcim rozdelením externých prerušení:

  • Level Interrupts - Prerušenie je vyvolané zakaždým, keď sa na vstupe objaví signál konkrétnej úrovne (HIGH alebo LOW). Pri tomto type prerušenia je dobré dať pozor na to, že pri nezmenenom signále môže k prerušeniu dochádzať opakovane aj počas ošetrovania predchádzajúceho prerušenia.

  • Edge Interrupts - Prerušenie je vyvolané vtedy, keď dôjde k zmene jednej úrovne signálu na druhú (napr. ak dôjde k zmene úrovne z HIGH na LOW alebo z LOW na HIGH).

Každý mikrokontrolér je špecifický tým, že na zachytenie externého prerušenia z pripojeného zariadenia nie je možné použiť každý digitálny pin. Vždy je preto potrebné overiť si možnosti mikrokontroléra v jeho dokumentácii. V prípade mikrokontrolérov Arduino sa je možné orientovať pomocou tabuľky XXX.

Board Digital Pins Usable For Interrupts
Uno, Nano, Mini, other 328-based 2, 3
Uno WiFi Rev.2 all digital pins
Mega, Mega2560, MegaADK 2, 3, 18, 19, 20, 21
Micro, Leonardo, other 32u4-based 0, 1, 2, 3, 7
Zero all digital pins, except 4
MKR Family boards 0, 1, 4, 5, 6, 7, 8, 9, A1, A2
Due all digital pins
101 all digital pins (Only pins 2, 5, 7, 8, 10, 11, 12, 13 work with CHANGE)

V porovnaní s mikrokontrolérom ESP8266 je na tom ATmega 328P horšie, pretože je možné použiť piny GPIO0GPIO15. To umožňuje naraz sledovať až 16 rozličných prerušení.

Pros and Cons of Interrupts

Výhody: * lepší reakčný čas v porovnaní s polling-om * šetrenie zdrojov

Nevýhody:

  • Prerušenia sa výrazne horšie ladia, pretože k prerušeniu môže dôjsť aj počas ladenia programu. A vy zrazu neviete, koľko prerušení bolo vykonaných medzi krokovaním aktuálnej časti programu.

Additional Sources