Během cvičení v této části kurzu Arduino se podíváme na nejvhodnější způsob připojení klávesnice k našemu zařízení v praxi.
Pomocí všech dosud probraných komponent sestavíme poplašné centrum, jehož hlavním mozkem bude Arduino.
Objednejte si sadu prvků a začněte se učit v praxi! Kliknutím sem přejdete do obchodu >>
Na úvod bych rád dodal, že tento článek „vpašovává“ další novinku. Při programování poplachového centra vám ukážu, jak se vypořádat s programováním zařízení, které musí provádět celou řadu operací. Použitím stavového automatu můžeme kód výrazně zjednodušit a vyhnout se vnořeným podmínkám. Zjistíte také, že se správným přístupem budete moci pracovat bez přerušenímůžete vytvořit dobře fungující program.
Maticová klávesnice pro Arduino
Připojení každého tlačítka k Arduino nás zatím stálo jeden vstup. S jedním, dvěma nebo třemi tlačítky to nebyl problém. Někdy však chceme mít možnost zadat do obvodu více údajů. Například zadání PIN kódu pro alarm vyžaduje nejméně 10 tlačítek (číslice 0 až 9) a v ideálním případě bychom chtěli mít k dispozici ještě několik dalších tlačítek (např. pro zrušení nebo potvrzení operací).
Jednoduše řečeno – potřebujeme numerickou klávesnici:
Samozřejmě by bylo nepohodlné a někdy i nemožné připojit každou klávesu na vlastní pin (zejména u větších klávesnic). Naštěstí existuje chytré řešení, jak výrazně snížit počet potřebných pinů.
Struktura maticové klávesnice
To nám umožňuje kontrolovat
16 tlačítek s 8 datovými linkami!
Když stiskneme tlačítko, zkratujeme sloupec s řádkem. Na obrázku výše například stisknutí tlačítka „1“ zkratuje řádek P1 se sloupcem P5. Pokud však stisknete klávesu „4“, řádek P2 se zkratuje se sloupcem P5. Kontrolou spojení mezi řádky P1-P4 a sloupci P5-P8 můžeme určit, zda a které tlačítko bylo stisknuto.
Maticová klávesnice v praxi
Při cvičeních budeme používat jednoduchou verzi klávesnice (bez předního panelu), kde jsou viditelné vnitřní spoje. To umožňuje těm, kterým není výše popsaný princip čtení jasný, vidět „zblízka“, jak vše vypadá:
Je čas převést klávesnici do praxe!
Hotové sady pro kurzy Forbot
Komponenty pro cvičení z kurzu Arduino (úroveň 2) jsou k dispozici jako hotové sady! Patří sem programovatelné diody, analogové a digitální teploměry, 7-segmentové displeje a senzor pohybu (PIR).
Knihovna KeyPad
V případě Arduino samozřejmě najdeme hotovou knihovnu, která nám používání takových klávesnic ještě více usnadní. V tomto případě to bude knihovna s názvem Keypad. Najdete ji přímo ve správci knihovny nebo na GitHubu: https://github.com/Chris–A/Keypad
Informace o instalaci knihoven najdete v tomto článku:
Kurz Arduino II – #2 – RGB LED diody (tradiční a WS2812)
První použití numerické klávesnice
Nejprve má smysl napsat testovací program, který zkontroluje, zda je tlačítko stisknuté. V takovém případě je příslušný znak odeslán do počítače prostřednictvím UART.
Nejprve připojíme klávesnici k Arduino pomocí pinů 2 až 9, jak je znázorněno na obrázku níže. Pořadí pinů je důležité, abychom mohli správně číst stisknutá tlačítka.
Klávesnici lze zapojit do kontaktní desky (klávesy pak ale směřují dolů). Proto se vyplatí použít kabely a připojit je přímo k Arduinu. K tomu je třeba „vytvořit“ 8 mužských/ženských linií (jako v předchozím článku):
V mém případě to celé vypadalo takto po zapojení a položení:
Nyní můžeme přejít k programování. Nejprve knihovnu integrujeme a zadáme všechny potřebné informace o naší klávesnici a jejím připojení:
#include //knihovna klávesnice
const byte ROWS = 4; // kolik řádků
const byte COLS = 4; //kolik sloupců
byte rowPins[ROWS] = {5, 4, 3, 2}; //piny řádků
byte colPins[COLS] = {6, 7, 8, 9}; //piny sloupců
Tato pasáž je natolik jasná, že ji pravděpodobně není třeba podrobněji vysvětlovat. Zmatek mohou způsobit pouze čísla pinů, která jsem uvedl (nebo jejich pořadí). To bude jasné o něco níže.
Dalším krokem je namapování klávesnice, tj. přiřazení určitých znaků klávesám. Přijal jsem toto pořadí:
Tyto informace poskytujeme následovně:
char keys[ROWS][COLS] = { //mapování klávesnice
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
Tento zápis se může zdát neintuitivní, ale pomáhá uspořádat program. Ti, kteří již mají zkušenosti s programováním, samozřejmě poznají, že se jedná o jednoduchou dvourozměrnou tabulku.
K tabulkám se vrátíme v kurzu Arduino – pokud je tedy někdo nezná,
pak se není čeho obávat.
Posledním krokem při konfiguraci je vytvoření nového objektu typu Keypad, v našem případě se bude jmenovat Klávesnice:
Keypad klávesnice = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); //inicializace klávesnice
Analogickou operaci jsme museli provést při deklarování nové světelné řady během cvičení v článku o programovatelných diodách WS2812. Jakmile je klávesnice deklarována, můžete pokračovat – tedy číst znaky.
Není třeba nastavovat piny
ke kterým je klávesnice připojena jako vstupy.
Celý kód vypadá takto:
#include //knihovna klávesnice
const byte ROWS = 4; // kolik řádků
const byte COLS = 4; //kolik sloupců
byte rowPins[ROWS] = {5, 4, 3, 2}; //piny řádků
byte colPins[COLS] = {6, 7, 8, 9}; //piny sloupců
char keys[ROWS][COLS] = { //mapování klávesnice
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
Keypad klávesnice = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); //inicializace klávesnice
void setup(){
Serial.begin(9600);
}
void loop(){
char klávesa = klávesnice.getKey();
if (klávesa){
Serial.println(klávesa);
}
}
V hlavní smyčce programu vytvoříme proměnnou pro uložení znaku (typu char) a poté do ní přiřadíme hodnotu načtenou z klávesnice. Tento znak získáme při volání příkazu klávesnice.getKey(). Zkontrolujeme také, zda byl znak skutečně přijat, a pokud ano, odešleme jej prostřednictvím příkazu UARTdo počítače.
Podmínka if (klávesa) je splněna, pokud se pod proměnnou klávesa nachází libovolný znak.
Jakákoli hodnota větší než 0 v podmínce je považována za PRAVDA.
Po nahrání programu se na monitoru sériového rozhraní zobrazí znaky přiřazené právě stisknutým tlačítkům:
Pokud počítač zobrazuje jiné znaky než na předchozím mapovém výkresu, je vhodné znovu zkontrolovat připojení. Pokud je pořadí řádků obrácené, může funkce nesprávně interpretovat stisknutá tlačítka.
Dalším způsobem, jak takovou chybu opravit, je změnit hodnotu v mapovací tabulce. Pokud bychom například chtěli obrátit klávesnici, bylo by třeba vložit následující kód:
char keys[ROWS][COLS] = { //mapování klávesnice
{'*','0','#','D'},
{'7','8','9','C'},
{'4','5','6','B'},
{'1','2','3','A'}
};
Samozřejmě jsou možné i mnohem „bizarnější“ kombinace. Kód se pak ale stává méně čitelným. To je přesně důvod, proč jsem na začátku trval na použití určitých čísel pinů a sekvencí připojení.
Doslovné dvojče výše uvedeného programu naleznete v příkladech dodaných s knihovnou. Jsou tam i další zajímavé programy – doporučuji vám, abyste si je sami vyzkoušeli. Ve zbytku tohoto článku se budeme zabývat programováním jednoduché poplachové centrály.
Řídicí jednotka alarmu na Arduino
Náš poplašný systém se skládá z numerické klávesnice, zvukového signálu, světelného signálu, detektoru pohybu (PIR) a senzoru otevření dveří (jazýčkový spínač).
Použité senzory alarmu byly popsány v předchozí části kurzu Arduino.
Celá věc může samozřejmě fungovat mnoha různými způsoby. Rozhodl jsem se, že můj alarm bude fungovat následovně: po zapnutí napájení přejdeme do stavu pohotovosti. Poplašný systém nedělá nic, jen čeká na aktivaci. Stisknutím tlačítka A (jako u alarmu) se spustí proces zastřežení.
Po několika vteřinách (čas na opuštění pokoje) se aktivuje poplašný systém – od té chvíle je náš pokoj hlídán. Jakmile zjistíme jakýkoli pohyb v místnosti, alarm okamžitě začne signalizovat ohrožení.
Když zjistíme otevření dveří, dáme uživateli několik sekund na deaktivaci alarmu zadáním čtyřmístného kódu. Pokud je PIN kód zadán nesprávně nebo není zadán vůbec, spustí se také alarm.
Navíc je vše naprogramováno tak, aby alarm běžel bez přerušení, plynule a bez zpoždění. A to vše pomocí nastavení jednoduchého stavového stroje.
Co je to stavový stroj?
V kontextu Arduino označujeme stavový stroj (nebo konečný stavový stroj) jako specifickou metodiku psaní programů, která nám umožňuje snadno a přehledně implementovat v zařízení různé funkce, které probíhají v pevně daném pořadí.
To nám umožňuje vyhnout se vnořeným dlouhým podmíněným příkazům. Z teoretického hlediska je toto téma mnohem složitější – zájemce odkazuji na Wikipedii. Prozatím postačí volný odkaz na tuto metodu.
Zde se zaměříme na praktické využití stavového stroje,
Teorie není nutná. Celá věc je velmi intuitivní.
Na základě výše uvedeného popisu fungování alarmu můžeme rozlišit 4 stavy:
- Pohotovostní režim – poplašný systém čeká na aktivaci.
- Monitorování – systém hlídá naše prostory.
- Vypnutí alarmu – poplašný systém čeká na zadání správného pinu.
- Poplachová signalizace – poplašný systém vydává zvukové a světelné signály.
Tyto funkce mohou samozřejmě následovat pouze ve správném pořadí. Nestane se, že bychom poplašný systém deaktivovali, když je v pohotovostním režimu apod. To je nejlépe patrné z následujícího schématu (nejedná se o stavový diagram, ale o jednoduché grafické znázornění výše uvedených popisů):
Jak vidíte, program by se mohl snadno skládat ze 4 nezávislých funkcí, které na sebe přímo navazují – samozřejmě v pevném pořadí. Začněme tedy psát kód.
Kód jednoduché ústředny alarmu na Arduino
Doufám, že je to zřejmé, ale pro jistotu bych to rád zdůraznil: Následující cvičení je pouze příkladem, zábavným a hobby projektem. Rozhodně se nejedná o profesionální a průmyslové řešení. Jde o učení!
Základní struktura programu je uvedena níže. Na začátku jsem již zadal informace o senzorech a bzučáku, které budou použity později. Také jsem prohlásil, že klávesnice – připojení zůstala stejná jako při prvním použití.
Velmi důležitá je proměnná StatusAlarm, která nás informuje o aktuálním stavu našeho zařízení. Jeho hodnota určuje, které operace se právě provádějí. V hlavní smyčce používáme také konstrukci spínací skříňky popsanou v Kurzu Arduino úroveň I.
Pomocí tohoto switche se budeme pohybovat mezi kódem,
který se bude provádět v závislosti na aktuálním stavu obvodu.
#define BZUČÁK 11
#define JAZÝČKOVÝ SPÍNAČ 10
#define PIR 1
#include //knihovna klávesnice
const byte ROWS = 4; // kolik řádků
const byte COLS = 4; //kolik sloupců
byte rowPins[ROWS] = {5, 4, 3, 2}; //piny řádků
byte colPins[COLS] = {6, 7, 8, 9}; //piny sloupců
char keys[ROWS][COLS] = { //mapování klávesnice
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
Keypad klávesnice = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); //inicializace klávesnice
volatile int stavAlarmu = 1;
void setup() {
pinMode(BZUČÁK, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP);
pinMode(PIR, INPUT_PULLUP);
}
void loop() {
switch(stavAlarmu) { //Provést akci specifickou pro daný stav
case 1:
//Pohotovostní stav
break;
case 2:
//Monitorování
break;
case 3:
//Odzbrojování
break;
case 4:
//Signalizace alarmu
break;
}
}
Stav 1: pohotovostní stav alarmu
Během tohoto stavu by měl přístroj signalizovat svou připravenost, např. rozsvícením LED diody, a po stisknutí tlačítka „A“ by se měl alarm přepnout do stavu 2 – po vyčkání na čas opuštění místnosti. Použitá dioda je pásek diody RGB popsaný v prvním článku této série. Připojíme je na pin A0.
Program samozřejmě vyžaduje také přidání nové knihovny a inicializaci pásu. Když je zařízení v prvním stavu, svítí jedna z LED diod také zeleně.
#define BZUČÁK 11
#define JAZÝČKOVÝ SPÍNAČ 10
#define PIR 1
#include //
knihovna klávesnice
#include //knihovna od LED pásky
const byte ROWS = 4; //kolik řádků
const byte COLS = 4; //kolik sloupců
byte rowPins[ROWS] = {5, 4, 3, 2}; //piny řádků
byte colPins[COLS] = {6, 7, 8, 9}; //piny sloupců
char keys[ROWS][COLS] = {//mapování klávesnice
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
Keypad klávesnice = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); //inicializace klávesnice
Adafruit_NeoPixel pásky = Adafruit_NeoPixel(8, A0, NEO_GRB + NEO_KHZ800); //konfigurace LED pásky
volatile int stavAlarmu = 1;
void setup() {
pinMode(BZUČÁK, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP);
pinMode(PIR, INPUT_PULLUP);
pásky.begin(); //inicializace pásky
pásky.show();
}
void loop() {
switch(stavAlarmu) { //Provedení akce specifické pro daný stav
case 1:
//Pohotovostní stav
pásky.setPixelColor(0, pásky.Color(0, 15, 0)); // dioda 1 svítí zeleně
pásky.show();
break;
case 2:
//Monitorování
break;
case 3:
//Odzbrojování
break;
case 4:
//Signalizace alarmu
break;
}
}
Nyní je třeba přidat možnost odjištění. Připomínka: Pokud stiskneme tlačítko označené „A“ (například Alarm), musíme přepnout do stavu 2. Za tímto účelem přidáme odpovídající podmínku v rámci stavu 1, fragment vypadá takto:
char klávesa = 0;
switch(stavAlarmu) { //Provedení akce specifické pro daný stav
case 1:
//Pohotovostní stav
pásky.setPixelColor(0, pásky.Color(0, 15, 0)); // dioda 1 svítí zeleně
pásky.show();
klávesa = klávesa.getKey();
if (klávesa == 'A') { //Má být alarm zapnutý?
stavAlarmu = 2;
}
break;
case 2:
//Monitorování
pásky.setPixelColor(0, pásky.Color(15, 0, 0)); // dioda 1 svítí červeně
pásky.show();
break;
Přiřazení proměnné stavAlarmu = 2; způsobí, že se program v dalším cyklu smyčky přepne do stavu střežení. Samozřejmě má smysl, jak bylo zamýšleno, přidat výše zmíněných několik sekund pro opuštění místnosti. Pro dosažení lepšího efektu jsem se rozhodl zde vytvořit dlouhý světelný efekt, který trvá asi 10 sekund.
V tomto případě přerušení programu pomocí funkce zpoždění nemá negativní vliv na program.
Do programu jsem také přidal funkci, která vypne všechny LED diody. Ve druhém stavu (pohotovostní režim) bylo také velmi malé zpoždění. Díky tomu máme jistotu, že zařízení funguje (blikající LED dioda) a je dostatečně malé, aby nerušilo zbytek programu.
V současné době by měl kód vypadat takto:
Vyzkoušejte alarmový obvod v praxi (červená LED dioda skutečně bliká silněji):
Stav 2: Monitorování místnosti
Pokud je zařízení ve druhém stavu, mělo by kromě blikající LED diody neustále kontrolovat stav senzorů. Téma přerušení v tuto chvíli ponecháme stranou – můžeme se bez něj obejít.
Před zpracováním programu musí být samozřejmě připojeny požadované senzory. Vývody jsou již v programu definovány pomocí direktivy #define. Pro další experimenty stačí připojit oba senzory (jazýčkový spínač a PIR) k naší základní desce:
#define BZUČÁK 11
#define JAZÝČKOVÝ SPÍNAČ 10
#define PIR 1
#include //knihovna klávesnice
#include //knihovna LED pásky
const byte ROWS = 4; // kolik řádků
const byte COLS = 4; //kolik sloupců
byte rowPins[ROWS] = {5, 4, 3, 2}; //piny řádků
byte colPins[COLS] = {6, 7, 8, 9}; //piny sloupců
char keys[ROWS][COLS] = { //mapování klávesnice
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
Keypad klávesnice = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); //inicializace klávesnice
Adafruit_NeoPixel pásky = Adafruit_NeoPixel(8, A0, NEO_GRB + NEO_KHZ800); //konfigurace LED pásky
volatile int stavAlarmu = 1;
void setup() {
pinMode(BZUČÁK, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP);
pinMode(PIR, INPUT_PULLUP);
pásky.begin(); //inicializace pásky
pásky.show();
}
void loop() {
char klávesa = 0; //proměnná pro uložení znaků klávesnice
int i = 0; //pomocná proměnná pro smyčku
switch(stavAlarmu) { //Provedení akce specifické pro daný stav
case 1:
//pohotovostní stav
pásky.setPixelColor(0, pásky.Color(0, 15, 0)); // Dioda 1 svítí zeleně
pásky.show();
klávesa = klávesnice.getKey();
if (klávesa == 'A') { //Mám aktivovat alarm?
for (i = 1; i < 8; i++) {
Streifen.setPixelColor(i, pásky.Color(0, 0, 15)); // Dioda č. i svítí modře
pásky.show();
delay(710);
} // provedení této smyčky trvá přibližně 5 sekund
for (i = 1; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(15, 0, 0)); // Dioda č. i svítí červeně
pásky.show();
delay(710);
} // provedení této smyčky trvá přibližně 5 sekund
vypniDiody();
stavAlarmu = 2;
}
break;
case 2:
//Monitorování
pásky.setPixelColor(7, pásky.Color(15, 0, 0)); //Dioda č. 8 svítí červeně
pásky.show();
delay(50);
pásky.setPixelColor(7, pásky.Color(0, 0, 0)); //Dioda č. 8 vypnuta
pásky.show();
delay(50);
break;
case 3:
//Odzbrojování
break;
case 4:
//Sygnalizace alarmu
break;
}
}
void vypniDiody() {
int i = 0;
for (i = 0; i < 8; i++){
pásky.setPixelColor(i, pásky.Color(0, 0, 0)); //Dioda LED 1 vypnuta
}
pásky.show();
}
V dalším kroku přidáme dvě podmínky. Detekce pohybu by měla okamžitě spustit alarm, zatímco otevření dveří (jazýčkový spínač) by nám mělo umožnit deaktivaci systému. To lze velmi snadno provést pomocí proměnné StatusAlarm.
case 2:
//Monitorování
pásky.setPixelColor(7, pásky.Color(15, 0, 0)); //Dioda č. 8 svítí červeně
pásky.show();
delay(50);
pásky.setPixelColor(7, pásky.Color(0, 0, 0)); //Dioda č. 8 je vypnuta
pásky.show();
delay(50);
if (digitalRead(PIR) == HIGH) {
stavAlarmu = 4; //Okamžitě spustíme alarm
} else if (digitalRead(JAZÝČKOVÝ SPÍNAČ) == HIGH) {
stavAlarmu = 3; //Šance na odzbrojení
}
break;
Po spuštění programu se samozřejmě neprojeví žádné nové účinky, protože stavy 3 a 4 ještě nemají žádný účinek. Proto nejprve obejdeme stav 3 tím, že obvod deaktivujeme. Nejprve přidáme do stavu 4 několik řádků, které spustí alarm. Zatím jen lehký efekt.
Stav 4: Signalizace alarmu
Program v současné době neumožňuje ukončit režim alarmu. V této části kódu tedy můžete „upustit páru“, nedochází zde k žádným prodlevám, které by nám mohly uškodit. Pro začátek přidám blikání ve dvou barvách, červené a modré:
case 4:
//Signalizace alarmu
for (i = 0; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(255, 0, 0)); //Dioda č. i svítí červeně
}
pásky.show();
delay(100);
for (i = 0; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(0, 0, 255)); //Dioda č. i svítí modře
}
pásky.show();
delay(100);
break;
}
Kód je tak jednoduchý, že není třeba nic vysvětlovat. Pokud má někdo pochybnosti o tom, co se zde děje, doporučuji podívat se na začátek článku. Arduino (úroveň II) abyste se vrátili zpět. Od této chvíle by zařízení mělo spustit alarm, když je aktivován alarm a když PIR senzor detekuje pohyb:
Stav 3: Vstup pinů
Nastal čas rozšířit program o jednu z jeho nejdůležitějších funkcí, a to deaktivaci alarmu. Tento problém vyřešíme ve dvou krocích. Zpočátku se budeme zabývat pouze zadáním kódu PIN.
Později přidáme čítač, který kontroluje,
zda již vypršel čas pro zadání kódu.
int pinAlarmPozice = 1;
char pinCifra1 = '1';
char pinCifra2 = '2';
char pinCifra3 = '3';
char pinCifra4 = '4';
Jak jsem již zmínil, v této fázi kurzu nepoužíváme tabulky, takže jsem kód zapsal do 4 samostatných proměnných. Pokud budeme v některém z příštích článků pracovat s tabulkami, můžeme se sem vrátit a napsat kód elegantněji.
V tuto chvíli bych však chtěl co nejjednodušeji ukázat.
jak tento mechanismus kontroly kódu funguje.
Jakmile deklarujeme kód (v tomto případě 1234), můžeme provést kontrolu ve stavu 3. Mechanismus funguje následovně – komentáře ke kódu by měly vše objasnit:
case 3:
//Odzbrojování
klávesa = klávesnice.getKey();
if (klávesa) {
//Je další uvedená cifra správná?
if (pinAlarmuPozice
== 1 && klávesa == pinCifra1
1) { //Pokud zkontrolujeme 1. pozici PINu
pinAlarmuPozice
++; //Číslo správné, lze zkontrolovat u dalšího
} else if (pinAlarmuPozice
== 2 && klávesa == pinCifra2
2) { //Pokud zkontrolujeme 2 pozice PINu
pinAlarmuPozice
++; //Číslo správné, lze zkontrolovat u dalšího
} else if (pinAlarmuPozice
== 3 && klávesa == pinCifra3
3) { //Pokud zkontrolujeme 3 pozice PINu
pinAlarmPosition
++; //Číslo správné, lze zkontrolovat u dalšího
} else if (pinAlarmuPozice
== 4 && klávesa == pinCifra4
Pokud zkontrolujeme 4 pozice pinu stavAlarmu = 1; //Všechny 4 číslice kódu jsou správné
pinAlarmuPozice
= 1; //Resetujeme informace o zadávaném PINu
} else {
stavAlarmu = 4; //Chyba kódu PIN - spuštění alarmu
pinAlarmuPozice
= 1; //Resetujeme informace o zadávaném PINu
}
}
break;
Stručně řečeno, tento mechanismus si pamatuje, kolik správných znaků kódu již bylo zadáno do proměnné pinAlarmuPozice. Tímto způsobem víme, se kterou proměnnou máme aktuálně porovnávat zadaný znak.
Pokud je čtvrtý zadaný znak správný, znamená to, že celý kód byl správný – můžeme se vrátit do stavu 1 („Standby“). Pokud někde při zadávání kódu uděláme chybu, přepneme se do stavu 4 a spustíme alarm.
V tomto okamžiku by měl celý kód vypadat takto:
#define BZUČÁK 11
#define JAZÝČKOVÝ SPÍNAČ 10
#define PIR 1
#include //knihovna klávesnice
#include //knihovna led pásky
const byte ROWS = 4; // kolik řádků
const byte COLS = 4; //kolik sloupců
byte rowPins[ROWS] = {5, 4, 3, 2}; //piny řádků
byte colPins[COLS] = {6, 7, 8, 9}; //piny sloupců
char keys[ROWS][COLS] = { //mapování klávesnice
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
Keypad klávesnice = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); // inicializace klávesnice
Adafruit_NeoPixel pásky = Adafruit_NeoPixel(8, A0, NEO_GRB + NEO_KHZ800); //konfigurace led pásky
volatile int stavAlarmu = 1;
int pinAlarmuPozice = 1;
char pinCifra1 = '1';
char pinCifra2 = '2';
char pinCifra3 = '3';
char pinCifra4 = '4';
void setup() {
pinMode(BZUČÁK, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP);
pinMode(PIR, INPUT_PULLUP);
pásky.begin(); //inicializace pásky
pásky.show();
}
void loop() {
char klávesa = 0; //proměnná pro uložení znaků klávesnice
int i = 0; //pomocná proměnná pro smyčku
switch(stavAlarmu ) { //Provedení akce specifické pro daný stav
case 1:
//Pohotovostní stav
pásky.setPixelColor(0, pásky.Color(0, 15, 0)); //dioda č. 1 svítí zeleně
pásky.show();
klávesa = klávesnice.getKey();
if (klávesa == 'A') { //Aktivovat alarm?
for (i = 1; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(0, 0, 15)); //dioda č.i svítí modře
pásky.show();
delay(710);
} // provedení této smyčky trvá přibližně 5 sekund
for (i = 1; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(15, 0, 0)); //dioda č. i svítí červeně
pásky.show();
delay(710);
} // provedení této smyčky trvá přibližně 5 sekund
vypniDiody();
stavAlarmu = 2;
}
break;
case 2:
//Monitorování
pásky.setPixelColor(7, pásky.Color(15, 0, 0)); //dioda č. 8 svítí červeně
pásky.show();
delay(50);
pásky.setPixelColor(7, pásky.Color(0, 0, 0)); //dioda č. 8 vypnuta
pásky.show();
delay(50);
if (digitalRead(PIR) == HIGH) {
stavAlarmu = 4; //Okamžitě spustíme alarm
} else if (digitalRead(JAZÝČKOVÝ SPÍNAČ) == HIGH) {
stavAlarmu = 3; //Šance na odzbrojení
}
break;
case 3:
//Odzbrojování
klávesa = klávesnice.getKey();
if (klávesa) {
//Je další uvedená číslice správná?
if (pinAlarmuPozice pinAlarmuPozice == 1 && klávesa == pinCifra1) { //Pokud zkontrolujeme 1 pozici PINu
pinAlarmuPozice++; //Číslo správné, lze zkontrolovat u dalšího
} else if (pinAlarmuPozice == 2 && klávesa == pinCifra2) { //Pokud zkontrolujeme 2 pozice PINu
pinAlarmuPozice++; //Čislo správné, lze zkontrolovat u dalšího
} else if (pinAlarmuPozice == 3 && klávesa == pinCifra3) { //Pokud zkontrolujeme 3 pozice PINu
pinAlarmuPozice++; //Číslo správné, lze zkontrolovat u dalšího
pinAlarmuPozice == 4klávesa stavAlarmu = 1; //Všechny 4 číslice kódu jsou správné
pinAlarmuPozice = 1; //Resetujeme informace o zadávaném pinu } else {
stavAlarmu = 4; //Chyba kódu PIN - spustit alarm
pinAlarmuPozice = 1; //Resetujeme informace o zadávaném PINu
}
}
break;
case 4:
//Signalizace alarmu
for (i = 0; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(255, 0, 0)); //Dioda č. i svítí červeně
}
pásky.show();
delay(100);
for (i = 0; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(0, 0, 255)); //Dioda č. i svítí modře
}
pásky.show();
delay(100);
break;
}
}
void vypniDiody() {
int i = 0;
for (i = 0; i < 8; i++){
pásky.setPixelColor(i, pásky.Color(0, 0, 0)); //Dioda č. 1 vypnuta
}
pásky.show();
}
Jak tento kód funguje v praxi:
Ve druhé situaci je kód správný:
Odpočítávání do zadání pinu
Nyní je třeba napsat část kódu, která spustí alarm, pokud zadání kódu trvá příliš dlouho. Samozřejmě zde nemůžete napsat například delay(10000), protože taková instrukce by celý program zastavila a my bychom nemohli kód zkontrolovat.
Ale… můžeme zadat malé zpoždění, např. 50 ms. Tato hodnota by nezasahovala do kódu, pin bychom mohli zadávat neustále. Stačí tedy přidat podmínku, která zkontroluje, zda jsme byli ve stavu 3 (odzbrojení) více než 100 krát.
Každý vstup funkčního bloku trvá 50 ms, tj. 100 vstupů je výsledkem 5 sekund.
Pokud během této doby není kód zadán, musí být alarm aktivován.
Přenos do programu je velmi jednoduchý. Stačí přidat globální proměnnou:
int kolikCasuUplynulo = 0;
Počítá se, kolikrát jsme byli v bloku, abychom zkontrolovali PIN kód. Poté přidáme v rámci funkčního bloku (prováděného během stavu 3):
delay(100);
kolikCasuUplynulo++;
if (kolikCasuUplynulo >= 50) {
stavAlarmu = 4;
}
Každých 100 ms čekací doby zvýší hodnotu proměnné kolikCasuUplynulo. Pokud zaznamenáme 50 nebo více, další cyklus hlavní smyčky přejde do stavu 4 a spustí alarm. Přesto stojí za to se ujistit, že proměnná kolikCasuUplynulo se počítá od nuly, když nám alarm dává možnost zadat pin. Nejlépe je vložit tento kód do stavu 2, krátce před přechodem do stavu 3.
if (digitalRead(PIR) == HIGH) {
stavAlarmu = 4; // Okamžitě spustíme alarm
} else if (digitalRead(JAZÝČKOVÝ SPÍNAČ) == HIGH) {
kolikCasuUplynulo= 0; //Vynulování proměnné
stavAlarmu = 3; //Šance na odzbrojení
}
Konečná verze programu vypadá následovně:
#define BZUČÁK 11
#define JAZÝČKOVÝ SPÍNAČ 10
#define PIR 1
#include //knihovna klávesnice
#include //knihovna LED pásky
const byte ROWS = 4; // kolik řádků
const byte COLS = 4; //kolik sloupců
byte rowPins[ROWS] = {5, 4, 3, 2}; //piny řádků
byte colPins[COLS] = {6, 7, 8, 9}; //piny sloupců
char keys[ROWS][COLS] = { //mapování klávesnice
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
Keypad klávesnice = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); //inicializace klávesnice
Adafruit_NeoPixel pásky = Adafruit_NeoPixel(8, A0, NEO_GRB + NEO_KHZ800); //Konfigurace LED pásky
volatile int stavAlarmu = 1;
int pinAlarmuPozice = 1;
char pinCifra1 = '1';
char pinCifra2 = '2';
char pinCifra3 = '3';
char pinCifra4 = '4';
int kolikCasuUplynulo = 0;
void setup() {
pinMode(BZUČÁK, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP);
pinMode(PIR, INPUT_PULLUP);
pásky.begin(); //inicializace pásky
pásky.show();
}
void loop() {
char klávesa = 0; //proměnná pro uložení znaků klávesnice
int i = 0; //pomocná proměnná pro smyčku
switch(stavAlarmu) { //Provést akci specifickou pro daný stav
case 1:
//Pohotovostní stav
pásky.setPixelColor(0, pásky.Color(0, 15, 0)); //Dioda č. 1 svítí zeleně
pásky.show();
klácesa = klávesnice.getKey();
if (klávesa == 'A') { //Zapnout alarm?
for (i = 1; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(0, 0, 15)); //Diode 4. i svítí modře
pásky.show();
delay(710);
} // dokončení této smyčky bude trvat přibližně 5 sekund
for (i = 1; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(15, 0, 0)); //Dioda 4. i svítí červeně
pásky.show();
delay(710);
} // dokončení této smyčky bude trvat přibližně 5 sekund
vypniDiody();
stavAlarmu = 2;
}
break;
case 2:
//Monitorování
pásky.setPixelColor(7, pásky.Color(15, 0, 0)); //Dioda č. 8 svítí červeně
pásky.show();
delay(50);
pásky.setPixelColor(7, pásky.Color(0, 0, 0)); //Dioda č. 8 vypnuta
pásky.show();
delay(50);
if (digitalRead(PIR) == HIGH) {
stavAlarmu = 4; //Okamžitě spustíme alarm
} else if (digitalRead(JAZÝČKOVÝ SPÍNAČ) == HIGH) {
kolikCasuUplynulo= 0; //Vynulování proměnné
stavAlarmu = 3; //Šance na odzbrojení
}
break;
case 3:
//Odzbrojování
klávesa = klávesnice.getKey();
if (klávesa ) {
//Je další uvedená cifra správná?
if (pinAlarmuPozice == 1 && klávesa == pinCifra1) { //Pokud zkontrolujeme 1 pozici PINu
pinAlarmPosition++; //Číslo správné, lze zkontrolovat na dalším
} else if (pinAlarmuPozice == 2 && klávesa == pinCifra2) { //Pokud zkontrolujeme 2 pozici PINu
pinAlarmPosition++; //Číslo správné, lze zkontrolovat na dalším
} else if (pinAlarmuPozice == 3 && klávesa == pinCifra3) { //Pokud zkontrolujeme 3 pozici PINu
pinAlarmuPozice++; //Číslo správné, lze zkontrolovat na dalším
} else if (pinAlarmuPozice == 4 && klávesa == pinCifra4) { //Pokud zkontrolujeme 4 pozici PINu
stavAlarmu = 1; //Všechny 4 číslice kódu jsou správné
pinAlarmuPozice = 1; //Resetování informací o zadávaném PINu
} else {
stavAlarmu = 4; //Chyba kódu PIN - spustit alarm
pinAlarmuPozice = 1; //Resetování informací o zadávaném PINu
}
}
delay(100);
kolikCasuUplynulo++;
if (kolikCasuUplynulo >= 50) {
stavAlarmu = 4;
}
break;
case 4:
//Signalizace alarmu
for (i = 0; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(255, 0, 0)); //Dioda č. i svítí červeně
}
pásky.show();
delay(100);
for (i = 0; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(0, 0, 255)); //Dioda č. i svítí modře
}
pásky.show();
delay(100);
break;
}
}
void vypniDiody() {
int i = 0;
for (i = 0; i < 8; i++){
pásky.setPixelColor(i, pásky.Color(0, 0, 0)); //Dioda č.1 je vypnuta
}
pásky.show();
}
Jak tato část programu funguje v praxi:
Přidání zvukových efektů
Aby alarm plnil svůj úkol, musí být připojen bzučák – existuje verze s generátorem nebo bez něj. Rozhodl jsem se pro verzi bez generátoru a připojil ho na pin 11, jak bylo deklarováno dříve:
Pak jsem přidal dvě linky do 4. stavu obvodu, který je zodpovědný za signalizaci alarmu. Další informace o funkci tone() naleznete v části 3. části kurzu.
case 4:
//Signalizace alarmu
for (i = 0; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(255, 0, 0)); //Dioda č. i svítí červeně
}
pásky.show();
tone(BZUČÁK, 4300);
delay(100);
for (i = 0; i < 8; i++) {
pásky.setPixelColor(i, pásky.Color(0, 0, 255)); //Dioda č. i svítí modře
}
pásky.show();
tone(BZUČÁK, 3500);
delay(100);
break;
Konečné fungování celého alarmu si můžete prohlédnout na videu níže:
Další úkoly - pro dobrovolníky
Program se stal poměrně rozsáhlým, takže ho nebudu dále „natahovat“. Pokud však chcete, můžete do programu přidat nové funkce. Uvádím seznam věcí, které lze přidat:
- akustický signál při zapnutí alarmu,
- akustický signál při zadání pinu (stisknutí klávesy – „pípnutí“),
- samostatné tlačítko, které je spojeno s přerušením a resetuje alarm,
- rozsáhlejší zvukové efekty.
Shrnutí
V průběhu tohoto článku jsem se poprvé pokusil společně s vámi vytvořit dlouhý kód. Doufám, že i pro vás je tato forma článku užitečná! Dřív jsem všechny „strašil“ přerušováním a tady jsem je nepoužíval. Proč tomu tak je? Protože nemusíte vždycky!
Díky tomuto programu se podařilo odstranit zpoždění. V praxi to funguje tak, že se neustále prochází hlavní smyčka a provádějí se pouze operace, které jsou (v daném okamžiku) nezbytné. Proto se nemusíte obávat, že by Arduino signál ze senzoru přehlédlo.
Vyplatí se seznámit se s různými řešeními a vybrat si je pro konkrétní projekty. Pokud by naše zařízení mělo provádět mnoho dalších časově náročných operací, nepochybně by se zde uplatnilo přerušení.
Objednejte si sadu prvků a začněte se učit v praxi! Kliknutím sem přejdete do obchodu >>