Kurs Arduino II – #5 – klawiatura, własny system alarmowy

Kurs Arduino II – #5 –  klawiatura, własny system alarmowy

Podczas wykonywania ćwiczeń z tej części kursu Arduino sprawdzimy jak w praktyce najwygodniej podłączyć do naszego urządzenia klawiaturę.

Korzystając z wszystkich omówionych do tej pory elementów zbudujemy centralkę alarmową, której głównym mózgiem będzie Arduino.

Słowem wstępu dodam, że artykuł ten "przemyca" jeszcze jedną nowość. Podczas programowania centralki alarmowej pokażę w jaki sposób poradzić sobie z zaprogramowaniem urządzenia, które musi wykonywać całkiem sporo operacji. Dzięki użyciu maszyny stanów znacznie uprościmy kod i unikniemy zagnieżdżania warunków. Co więcej, okaże się, że odpowiednie podejście do problemu pozwoli stworzyć dobrze działający program nawet bez korzystania z przerwań.

Klawiatura matrycowa do Arduino

Do tej pory podłączenie każdego przycisku do Arduino "zabierało" nam jedno wejście. Przy jednym, dwóch lub trzech przyciskach nie stanowiło to problemu. Czasami jednak chcemy mieć możliwość wprowadzenia do układu większej ilości danych. Na przykład wpisanie kodu pin do alarmu wymaga minimum 10 klawiszy (cyfry od 0 do 9), a najlepiej gdybyśmy do dyspozycji mieli jeszcze kilka przycisków (np. do anulowania lub zatwierdzania operacji).

Po prostu - przydałaby się nam klawiatura numeryczna:

Przykład klawiatury numerycznej.

Przykład klawiatury numerycznej.

Oczywiście podłączenie każdego przycisku do osobnego pinu byłoby uciążliwe, a czasami wręcz niemożliwe (szczególnie przy większych klawiaturach). Na szczęście istnieje sprytne rozwiązanie, które pozwala znacznie zredukować ilość potrzebnych pinów.

Budowa klawiatury matrycowej

W klawiaturach matrycowych producenci łączą przyciski w kolumny oraz wiersze. Dzięki temu powstaje matryca połączeń. Może to przykładowo wyglądać tak:

Wewnętrzne połączenie sygnałów w klawiaturze.

Wewnętrzne połączenie sygnałów w klawiaturze.

W chwili, gdy wciskamy przycisk zwieramy jedną kolumnę z jednym wierszem. Przykładowo na powyższym rysunku wciśnięcie przycisku "1" zwiera wiersz P1 z kolumną P5. Natomiast wciśnięcie "4" zwiera wiersz P2 z kolumną P5. Sprawdzając połączenia między wierszami P1-P4 oraz kolumnami P5-P8 możemy wykryć, czy i jaki przycisk został wciśnięty.

Klawiatura matrycowa w praktyce

Podczas ćwiczeń wykorzystamy prostą wersję klawiatury (bez panelu przedniego), w której widać wewnętrzne połączenia. Dzięki temu osoby, dla których powyższa zasada działania odczytów nie jest jasna będą mogły sprawdzić "z bliska", jak to wszystko wygląda:

Pora uruchomić klawiaturę w praktyce!

Zestaw elementów do kursu

 999+ pozytywnych opinii  Gwarancja pomocy  Wysyłka w 24h

Części do wykonania ćwiczeć z kursu Arduino (poziom 2) dostępne są w formie gotowych zestawów! W komplecie m.in. diody programowalne, termometry analogowe i cyfrowe, wyświetlacze 7-segmentowe oraz czujnik ruchu (PIR).

Zamów w Botland.com.pl »

Biblioteka KeyPad

Oczywiście w przypadku Arduino znajdziemy gotową bibliotekę, która jeszcze bardziej ułatwi nam wykorzystywanie takich klawiatur. W tym wypadku będzie to biblioteka nazwana Keypad. Można ją znaleźć bezpośrednio w managerze bibliotek lub na GitHubie: https://github.com/Chris--A/Keypad

Pierwsze użycie klawiatury numerycznej

Na początek warto napisać program testowy, który będzie sprawdzał, czy jakiś przycisk jest wciśnięty. Jeśli tak będzie, to odpowiedni znak zostanie wysłany do komputera przez UART.

Na początek podłączamy klawiaturę do Arduino za pomocą pinów od 2 do 9 zgodnie z poniższym rysunkiem. Kolejność pinów jest ważna, abyśmy mogli poprawnie odczytywać wciśnięte przyciski.

Połączenie klawiatury z Arduino.

Połączenie klawiatury z Arduino.

Klawiaturę można wpiąć w płytkę stykową (przyciski będą wtedy skierowane jednak w dół). Warto więc wykorzystać przewody i podłączyć ją bezpośrednio do Arduino. W tym celu trzeba "stworzyć" 8 przewodów męsko-żeńskich (tak jak w poprzednim artykule):

Połączenie przewodów w celu uzyskania przejściówki do płytki stykowej.

Połączenie przewodów w celu uzyskania przejściówki do płytki stykowej.

U mnie, całość po spięciu i ułożeniu przewodów wyglądała tak:

Pierwszy układ testowy z klawiaturą matrycową.

Pierwszy układ testowy z klawiaturą matrycową.

Teraz można przejść do programowania. Na początku dołączamy bibliotekę i podajemy wszystkie niezbędne informacje na temat naszej klawiatury i jej podłączenia:

Fragment ten jest na tyle przejrzysty, że chyba nie ma potrzeby rozpisywania się na jego temat. Jedynie podane przeze mnie numery pinów (a właściwie ich kolejność) mogą budzić zastanowienie. Akurat to wyjaśni się kawałek dalej.

Kolejnym etapem jest mapowanie klawiatury, czyli przypisanie konkretnych znaków pod przyciski. najwygodniej będzie jeśli przycisk opiszemy w standardowy sposób. Ja przyjąłem taką kolejność:

Przyjęte mapowanie klawiszy.

Przyjęte mapowanie klawiszy.

Informacje te podajemy w następujący sposób:

Zapis ten może wydawać się mało intuicyjny, jednak pozwala uporządkować program. Osoby, które mają wcześniejsze doświadczenia z programowaniem poznają oczywiście, że jest to zwykła tablica dwuwymiarowa.

Ostatni krok podczas konfiguracji, to utworzenie nowego obiektu typu Keypad, u nas będzie nosił on nazwę klawiatura:

Analogiczną operację musieliśmy wykonywać podczas deklaracji nowej linijki świetlnej podczas ćwiczeń z artykułu na temat diod programowalnych WS2812. Gdy klawiatura będzie zadeklarowana można przejść dalej - czyli do odczytywania znaków.

Cały kod wygląda następująco:

W pętli głównej programu tworzymy zmienną do przechowywania znaku (typu char), a następnie przypisujemy jej wartość odczytaną z klawiatury. Znak ten otrzymamy po wywołaniu polecenia klawiatura.getKey(). Dalej sprawdzamy, czy faktycznie odebrano znak i jeśli tak, to odsyłamy go do komputera przez UART.

Po wgraniu programu w monitorze portu szeregowego wyświetlać będą się znaki przypisane do aktualnie wciskanych klawiszy:

Efekt działania pierwszego programu.

Efekt działania pierwszego programu.

Jeśli w komputerze wyświetlają się inne znaki, niż te na wcześniejszym rysunku z mapowaniem, to warto sprawdzić jeszcze raz połączenia. Zamiana kolejności przewodów sprawi, że funkcja będzie błędnie interpretować wciskane klawisze.


Innym sposobem naprawy takiego błędu byłaby zmiana wartości w tablicy mapowania. Np. jeśli chcielibyśmy odwrócić klawiaturę, to należałoby umieścić tam poniższy kod:

Oczywiście możliwe są również znacznie bardziej "udziwnione" kombinacje. Jednak wtedy kod staje się mniej czytelny. To właśnie dlatego nalegałem, aby na początku wykorzystać konkretne numery pinów i kolejność połączeń.


Dosłownie bliźniaczy program do powyższego, można znaleźć w przykładach dostarczonych wraz z biblioteką. Dodatkowo jest tam kilka innych ciekawych programów - zachęcam do samodzielnych testów. W dalszej części artykułu zajmiemy się już zaprogramowaniem prostej centralki alarmowej.

Centralka alarmowa na Arduino

Nasz system alarmowy będzie składał się z klawiatury numerycznej, sygnalizatora dźwiękowego, sygnalizatora świetlnego, czujnika ruchu (PIR) oraz czujnika otwarcia drzwi (kontaktron).

Oczywiście całość może działać na wiele różnych sposób. Ja postanowiłem, że mój alarm będzie działał następująco: po włączeniu zasilania wchodzimy w tryb gotowości. Alarm nic nie robi tylko czeka na jego uzbrojenie. Po wciśnięciu klawisza A (jak Alarm) następuje procedura uzbrajania.

Po kilku sekundach (czas na wyjście z pomieszczenia) alarm zaczyna działać - od tej pory nasze pomieszczenie jest pilnowane. W momencie, gdy wykryjemy ruch w pomieszczeniu alarm zaczyna natychmiast sygnalizować zagrożenie.

Czy alarm zdąży zauważyć szybkiego złodzieja?

Jeśli wykryjemy otwarcie drzwi, to dajemy użytkownikowi kilka sekund na rozbrojenie alarmu, które polega na wpisaniu 4 cyfrowego kodu. Jeśli kod pin będzie błędny lub nie zostanie wprowadzony, to alarm również zostanie uruchomiony.

Czym jest maszyna stanów?

W kontekście Arduino maszyną stanów (lub automatem skończonym) nazwiemy pewną metodykę pisania programów, która pozwala na łatwe i czytelne zaimplementowanie w urządzeniu różnych funkcji, które następują w ustalonej kolejności.

Dzięki temu unikamy zagnieżdżonych, długich instrukcji warunkowych. Temat od strony teorii jest znacznie bardziej złożony - zainteresowanych odsyłam do Wikipedii. Nam wystarczy teraz luźne nawiązanie do tej metody.

Biorąc za przykład powyższy opis działania alarmu możemy wyróżnić w nim 4 stany:

  1. Czuwanie - alarm czeka na uzbrojenie.
  2. Monitorowanie - system pilnuje naszego pomieszczenia.
  3. Rozbrajanie - alarm czeka na wprowadzenie poprawnego pinu.
  4. Sygnalizacja alarmu - alarm wydaje dźwięki i sygnały świetlne.

Oczywiście funkcje te mogą następować po sobie tylko w odpowiedniej kolejności. Nie zdarzy się sytuacja, że będziemy rozbrajać alarm, gdy jest w czasie czuwania itd. Najlepiej widoczne jest to na poniższym schemacie (nie jest to graf stanów, traktujmy to jako zwykłą, graficzną reprezentację powyższych opisów):

kursardu_5_4v2

Jak widać program spokojnie mógłby składać się z 4 niezależnych funkcji, które kierują do siebie nawzajem - oczywiście w ustalonej kolejności. Zacznijmy więc pisać kod.

Kod prostej centralki alarmowej na Arduino

Główny szkielet programu widoczny jest poniżej. Na początku wpisałem już informacje na temat czujników oraz buzzera, które będą później używane. Zadeklarowałem też klawiaturę - połączenia zostały takie same jak podczas jej pierwszego wykorzystania.

Bardzo ważna jest zmienna stanAlarmu, która będzie mówiła o aktualnym stanie, w którym jest nasze urządzenie. To jej wartość będzie decydowała o aktualnie wykonywanych operacjach. Dalej, w pętli głównej korzystamy z konstrukcji switch-case opisane w kursie Arduino, poziom I.

Stan 1: czuwanie alarmu

Podczas tego stanu urządzenie powinno sygnalizować swoją gotowość np. świeceniem diody, a po wciśnięciu klawisza "A" alarm powinien przejść do stanu 2 - po wcześniejszym odczekaniu czasu na wyjście z pomieszczenia. W roli diody użyjemy linijkę diod RGB opisaną w 1 artykule z tej serii.  Podłączamy ją do pinu A0.

Podłączenie linijki diod do Arduino.

Podłączenie linijki diod do Arduino.

Oczywiście program również wymaga dodania nowej biblioteki oraz inicjalizacji linijki. Co więcej podczas, gdy urządzenie jest w stanie pierwszym, to jedna z diod będzie świeciła na zielono.

Pora dodać możliwość uzbrajania. Przypomnę, że po wciśnięciu przycisku mapowanego jako "A" (jak alarm) mamy przejść do stanu drugiego. W tym celu wewnątrz stanu 1 dopisujemy odpowiedni warunek, fragment ten wygląda następująco:

To właśnie przypisanie zmiennej stanAlarmu = 2; sprawia, że program w następnym obiegu pętli przejdzie do stanu uzbrojonego. Oczywiście zgodnie z założeniami warto dopisać tutaj wspomniane kilka sekund na wyjście z pomieszczenia. Dla lepszego efektu postanowiłem, że w tym miejscu będę wykonywał długi efekt świetlny, który potrwa około 10 sekund.

Dodatkowo dodałem do programu funkcję gaszącą wszystkie diody. Wewnątrz stanu drugiego (czuwanie) pojawił się też bardzo mały delay. Dzięki niemu mamy pewność, że urządzenie działa (miganie diody), a z drugiej strony jest ono na tyle małe, że nie zaszkodzi reszcie programu.

W chwili obecnej kod powinien wyglądać następująco:

Test uzbrajania alarmu w praktyce (czerwono dioda w rzeczywistości miga wyraźniej):

Stan 2: monitorowanie pomieszczenia

Gdy urządzenie przebywa w drugim stanie, to oprócz migania diodą powinno ciągle sprawdzać stany czujników. Pomijamy kwestię przerwań w tym miejscu - tutaj obejdziemy się bez nich.

Przed edycją programu trzeba oczywiście podłączyć wymagane czujniki. Piny podane zostały już w programie z pomocą dyrektywy #define. Na potrzeby dalszych eksperymentów wystarczy oba czujniki (kontaktron i PIR) przymocować do naszej podstawki:

W kolejnym kroku dodajemy dwa warunki. Wykrycie ruchu ma natychmiast uruchamiać alarm, natomiast otwarcie drzwi (kontaktron) powinno dać nam szanse na wyłączenie układu. Korzystając ze zmiennej stanAlarmu można to zrobić bardzo prosto

Oczywiście po uruchomieniu programu nie zobaczymy nowych efektów, ponieważ stany 3 oraz 4 jeszcze nic nie robią. Dlatego na razie ominiemy stan 3 z rozbrajaniem układu. Najpierw dodajmy kilka linijek do stanu 4, które wywołają alarm. Na razie tylko świetlny.

Stan 4: sygnalizacja alarmu

Na chwile obecną program nie przewiduje, że można wyjść z trybu alarmu. W tej sekcji kodu można więc "zaszaleć", właściwe żadne opóźnienia nam nie zaszkodzą. Ja dodam na początek miganie w dwóch kolorach czerwonym i niebieskim:

Kod jest na tyle prosty, że nie ma co w nim tłumaczyć. Jeśli ktoś miałby wątpliwości, co się tutaj dzieje, to polecam wrócić do początku kursu Arduino (poziom II). Od tej pory, po aktywacji alarmu i wykryciu ruchu przez czujnik PIR, urządzenie powinno sygnalizować alarm:

Stan 3: Wpisywanie pinu

Pora na rozbudowanie programu o jedną z ważniejszych funkcji, czyli deaktywację alarmu. Problem ten rozwiążemy w dwóch stopniach. Na początku zajmiemy się tylko wpisywaniem kodu pin.

Na początku w sekcji ze zmiennymi globalnymi dodajemy następujące wartości:

Tak jak wspomniałem wcześniej, na tym etapie kursu jeszcze nie korzystamy z tablic, więc kod zapisałem w 4 osobnych zmiennych. Gdy w jednym z kolejnych artykułów przećwiczymy tablice, to będzie można wrócić do tego miejsca i napisać kod bardziej elegancko.

Gdy mamy już zadeklarowany kod (w tym przypadku 1234), to wewnątrz stanu 3 możemy dokonać jego sprawdzenia. Mechanizm działa następująco - komentarze kodu powinny wiele rozjaśnić:

Podsumowując mechanizm ten pamięta w zmiennej pinAlarmuPozycja ile podano już dobrych znaków z kodu. Dzięki temu wiemy, z którą zmienną aktualnie trzeba porównać wpisywany znak.

Gdy czwarty podany znak jest poprawny, to znaczy, że cały kod był poprawny - wtedy możemy wrócić do stanu 1 (czuwanie). Jeśli w którymś miejscu podczas wpisywania kodu się pomylimy, to przechodzimy do stanu 4 i uruchamiamy alarm.

Na ten moment cały kod powinien wyglądać następująco:

Działanie tego fragmentu kodu w praktyce:

Drugi przypadek dla kodu poprawnego:

Odliczanie czasu na wpisanie pinu

Pozostaje dopisać fragment kodu, który będzie odpowiadał za uruchomienie alarmu jeśli wpisanie kodu zajmowałoby nam z byt dużo czasu. Oczywiście nie można tutaj wpisać np. delay(10000), bo taka instrukcja zamroziłaby cały program i nie moglibyśmy sprawdzać kodu.

Ale... moglibyśmy wprowadzić małe opóźnienie np. 50 ms. Wartość ta nie zakłóci działania kodu, cały czas będziemy mogli wpisywać pin. Wystarczy więc dodać warunek, który sprawdzi, czy w stanie 3 (rozbrajanie) byliśmy więcej niż 100 razy.

Przełożenie tego do programu jest bardzo proste. Wystarczy dodać zmienną globalną:

Za jej pomocą zliczymy ile razy byliśmy wewnątrz bloku sprawdzającego poprawność kodu pin. Następnie wewnątrz bloku funkcji (wykonywanych podczas stanu 3) dodajemy:

Każde 100 ms odczekiwania zwiększy wartość zmiennej ileCzasuMinelo, gdy naliczymy 50 lub więcej, to w kolejnym obiegu pętli głównej przejdziemy do stanu 4 i włączymy alarm. Warto jeszcze zadbać, aby zmienna ileCzasuMinelo liczyła od zera, gdy alarm da nam szansę na podanie pinu. Najlepiej kod ten dodać wewnątrz stanu 2, tuż przed przejściem do 3.

Ostateczna wersja programu wygląda następująco:

Działanie tego fragmentu programu w praktyce:

Dodanie efektów dźwiękowych

Aby alarm  spełniał swoją rolę trzeba podłączyć buzzer - może być wersja z generatorem lub bez. Ja zdecydowałem się na wersję bez generatora i podłączyłem ją do pinu nr 11, tak jak było to wcześniej zadeklarowane:

Podłączenie buzzera do centralki alarmowej.

Podłączenie buzzera do centralki alarmowej.

Następnie dopisałem dwie linijki do 4 stanu układu, który odpowiada za sygnalizację alarmu. Więcej informacji na temat funkcji tone() znaleźć można w 3 części kursu.

Ostateczne działanie całego alarmu widać na poniższym filmie:

Zadania dodatkowe - dla chętnych

Program wyszedł dość rozbudowany, więc nie będę go teraz jeszcze bardziej "rozciągać". Chętni mogą jednak rozbudować program o nowe funkcje. Podaję listę rzeczy, które warto dodać:

  • sygnał dźwiękowy podczas uzbrajania alarmu,
  • sygnał dźwiękowy podczas podawania pinu (wciśnięcie klawisza - "bip"),
  • osobny przycisk podłączony pod przerwanie, który resetuje alarm,
  • bardziej rozbudowane efekty dźwiękowe.

Podsumowanie

Podczas tego artykułu starałem się pierwszy raz zbudować razem z Wami jeden, długi kod. Mam nadzieję, że taka forma artykułu również jest przydatna - dajcie znać w komentarzach! Poprzednio "straszyłem" wszystkich przerwaniami, a tutaj ich nie użyłem. Dlaczego? Bo nie zawsze trzeba!

Warto znać różne rozwiązania i dobierać je pod konkretne projekty. Gdyby nasze urządzenie wykonywało dużo więcej czasochłonnych operacji, to przerwania niewątpliwie znalazły by tutaj swoje zastosowanie.

Czy artykuł był pomocny? Oceń go:

Średnia ocena / 5. Głosów łącznie:

Nawigacja kursu

Autor kursu: Damian (Treker) Szymański

alarm, arduino, czujniki, kurs, kursArduino2

Trwa ładowanie komentarzy...