Kurs Arduino – #9 – Czujnik odległości HC-SR04, funkcje

Kurs Arduino – #9 – Czujnik odległości HC-SR04, funkcje

W tej części kursu Arduino wykorzystamy czujnik odległości HC-SR04, który cieszy się dużym zainteresowaniem wśród majsterkowiczów.

Przyszła również pora, aby zacząć pisać własne funkcje. Na koniec zajmiemy się także bardzo prostym elementem akustycznym - buzzerem.

Mam nadzieje, że część ta będzie ciekawa dla konstruktorów robotów oraz wszystkich, którzy chcieliby w swoich projektach wykorzystywać czujnik odległości, taki jak na poniższym zdjęciu.

Ard_9_4

Zanim jednak do tego dojdzie zajmiemy się opanowaniem nowych sztuczek programistycznych, które sprawią, że nasze programy będą jeszcze lepsze - zarówno dla Arduino, jak i dla osoby odpowiedzialnej za tworzenie programu, czyli dla Ciebie!

Zestaw elementów do kursu

Gwarancja pomocy na forum Błyskawiczna wysyłka

Teraz możesz kupić zestaw ponad 70 elementów niezbędnych do przeprowadzenia ćwiczeń z kursu u naszych dystrybutorów!

Kup w Botland.com.pl

Własne funkcje bez argumentów

Do tej pory nasz kod umieszczaliśmy w funkcjach setup() {} lub loop() {}. Pierwsza z nich dotyczyła ustawień, a druga była nieskończoną pętlą główną, która wykonywała się cały czas. Przykładowo, aby migać diodą podłączoną do pinu nr 13 należało napisać taki program:

Wyobraźmy sobie sytuację, w której nasz program jest bardzo rozbudowany, a miganie chcemy wykorzystać jako potwierdzanie wybranych operacji. Szybko okaże się, że wielokrotne powielanie fragmentu odpowiedzialnego za włączanie i wyłączanie diody jest czasochłonne dla programisty. Co gorsza, sprawia to również, że cały program jest znacznie trudniejszy w analizowaniu.

Deklarując zmienną możemy odwoływać się do niej wielokrotnie w łatwy sposób. Gdybyśmy mogli zapisywać pod łatwą nazwą całe ciągi operacji, to programy byłyby zdecydowanie czytelniejsze. Również ewentualne zmiany byłyby łatwiejsze. Tutaj pomocne będą właśnie funkcje.

Programując Arduino korzystamy z wielu gotowych funkcji np.:  digitalWrite(13, HIGH);. Wiemy, że zapis ten powoduje ustawienie stanu wysokiego (logiczne 1) na pinie numer 13. Jednak nie musimy wiedzieć w jaki sposób jest to realizowane. Analogicznie możemy tworzyć własne funkcje. Przykładowo zajmijmy się przytoczonym już miganiem.

Za tę operacje jest odpowiedzialny następujący fragment kodu:

Możemy go "wyciągnąć" poza  loop() {} i stworzyć z niego osobną funkcję. Jak to zrobić?

Na początku konieczne jest zadeklarowanie nazwy funkcji oraz jej typu. Robimy to np.: pod funkcją  loop() {}:

Jak widać przed nazwą funkcji, czyli w tym wypadku zamigajLED znajduje się typ funkcji. Jest to nic innego jak informacja, czy funkcja po zakończeniu działania zwraca jakąś wartość. Jeśli by tak było, to należałoby wpisać tam odpowiedni typ (zgodnie z typami zmiennych). Mogła to by być np.: liczba całkowita (int), znak (char), liczba niecałkowita (float) itd.

W tym wypadku znajduje się tam słowo void (z ang. pustka). Typ ten oznacza, że funkcja nie zwraca żadnej wartości. Dlaczego taki typ wybrałem? Zadaniem funkcji jest zamiganie diodą, operacja ta nie daje żadnego wyniku (oprócz tego widocznego dla naszego oka).

(W dalszej części artykułu omówione są inne przypadki)

Za nazwą funkcji pojawia się nawias okrągły. Na ten moment przyjmijmy, że w nawiasie tym nic nie ma. Dalej otwieramy nawias klamrowy i w jego wnętrzu umieszczamy kod, który ma się wykonać w momencie wywołania funkcji. W praktyce:

Zwróć uwagę, że w pętli  loop() {} wpisaliśmy nazwę naszej zmiennej (już bez przedrostka void). Działanie takie nazywamy wywołaniem funkcji. Wgraj program i sprawdź, czy działa zgodnie z poniższą animacją:

ArduinoLED_Blink

Wróżmy do założonej wcześniej sytuacji, w której migania używasz w licznych, bardzo różnych miejscach programu (do potwierdzania jakiejś operacji). Co byłoby, gdybyś nagle zechciał zmienić sekwencję świecenia lub np.: ilość mignięć? Musiałbyś zmieniać ten sam fragment kodu w kilkunastu miejscach. Gdy skorzystasz z własnej funkcji będzie to banalnie proste, wystarczy zmiana w tym jednym miejscu:

Zadanie domowe 9.1

Napisz funkcję, która sprawia, że dioda będzie  pulsować (stopniowe przygaszanie i rozjaśnianie). Skorzystaj oczywiście z wiadomości zdobytych podczas lekcji o sygnale PWM.

Arduino - własne funkcje z argumentami

Pora na odkrycie kolejnych tajemnic funkcji. Pamiętasz jedną z najczęściej wywoływanych przez nas funkcji Arduino? Prawdopodobnie będzie to zmiana stanu na konkretnym pinie, przykładowo:

W odróżnieniu od naszej funkcji  zamigajLED();, tutaj w nawiasach okrągłych wpisujemy jakieś dane. Może to być numer pinu, stan, który chcemy uzyskać lub wypełnienie sygnału PWM. Te informacje wykorzystywane są później wewnątrz funkcji do wykonania pewnych operacji.

Zwróć uwagę na użyte słownictwo, zarówno argumenty, jak i przekazywanie  nie zostały użyte przypadkowo. Są to określenia stosowane przez wszystkich programistów! Teraz pora napisać własną funkcję, na początek z jednym argumentem.

Załóżmy, że chcemy edytować naszą funkcję zamigajLED w taki sposób, aby podczas wywoływania możliwa była zmiana prędkości migania. W naszej deklaracji funkcji musimy dodać informację, że może ona przyjmować argument. Robimy to w nagłówku funkcji:

Od tej pory kompilator wie, że w momencie napotkania wywołania funkcji w okrągłych nawiasach ma spodziewać się liczby (typ int). Liczba ta zostanie przekazana do funkcji i będzie tam występowała pod nazwą czas.

W praktyce wnętrze funkcji będzie wyglądało tak:

Pora na ostatni element, czyli nowe wywołanie funkcji. Tutaj chyba nie będzie nic zaskakującego. Zwyczajnie w nawiasie wpisujemy liczbę, która będzie oznaczała czas przez jaki dioda ma być włączona i wyłączona. Cały kod prezentuje się tak:

Sprawdź, że podmieniając wartość w nawiasach możesz wpływać na częstotliwość migania. Pora na kolejny przykład.

Funkcje z argumentem - przykład 2

Do tej pory ciągłe miganie diody było widoczne, ponieważ wywoływaliśmy naszą funkcję w funkcji loop, która jest pętlą. Gdy przeniesiemy wywołanie funkcji do sekcji setup, to po starcie systemu zobaczymy tylko jedno mignięcie. Sprawdź:

Dokładnie tak działoby się, gdybyśmy chcieli wykorzystać funkcję np.: do potwierdzeni wciśnięcia przycisku. Wywołanie zamigajLED() powodowałoby jeden błysk.

Pora na przerobienie funkcji w taki sposób, aby oprócz zmiany czasu migania możliwe było również wpływanie na ilość mignięć. Na początku musimy zmienić deklarację funkcji:

Dodaliśmy argument ile, to on będzie odpowiadał za ilość mignięć. Jak widać, wpisany został po przecinku, poprzedzony oczywiście swoim typem (liczba całkowita, int). Parametr ten mógłby być innego typu, nie ma to żadnej różnicy. Ważne, aby deklaracja była odpowiednia.

Jak pewnie się domyślasz wykorzystanie drugiego argumentu jest bardzo proste. Zwyczajnie mamy do dyspozycji teraz drugą zmienną lokalną ile.

Mam nadzieję, że wiesz już, którą pętlę użyć, aby najłatwiej zapanować nad ilością mignięć? Jeśli nie, to odsyłam do poprzedniej części, w której omówiłem pętle for. Zakładam jednak, że materiał ten masz opanowany (lub zaraz nadrobisz braki) i przedstawiam nową funkcję zamigajLED:

Sprawdźmy cały kod:

Wszystko powinno działać. Teraz po starcie dioda musi zamigać dokładnie 5 razy. Jednak skąd Arduino wie jak rozróżnić informacje podane w nawiasach, co jest czasem, a co ilością powtórzeń?

Otóż zasada jest banalnie prosta - kolejne wartości oddzielone przecinkami są przypisywane do kolejnych argumentów opisanych w deklaracji funkcji. W powyższym przypadku pierwsza liczba będzie zawsze traktowana jako czas, a druga jako ilość powtórzeń.

Zadanie domowe 9.2

Napisz funkcję, która pozwala osobno regulować ilość mignięć, czas przez jaki dioda jest wyłączona oraz osobno, czas przez jaki dioda jest włączona.

Czy nasza funkcja może zostać jeszcze bardziej rozbudowana? Oczywiście! Czy nie byłoby wygodnie, aby oprócz deklaracji czasu i ilości powtórzeń możliwa była deklaracja pinu, na którym podłączona jest dioda?

Funkcje z argumentami - przykład nr 3

Pin, do którego podłączona jest dioda, to w naszym programie liczba. Wykorzystujemy go w dwóch miejscach podczas deklaracji danego pinu jako wyjście oraz podczas ustawiania stanu wysokiego lub niskiego. Czyli, liczba ta może być kolejnym argumentem naszej funkcji!

Na początku podłączmy drugą diodę do innego pinu, np.: nr 8:

Podłączenie diody do Arduino.

Podłączenie diody do Arduino.

Nie będę się już tutaj rozpisywał, od razu przedstawiam cały przykład:

Sprawdźmy, czy działa? Niezbyt, prawda? Miga tylko dioda podłączona do pinu 13, a ta pod 8 nie chce dać znaku życia. Dlaczego? Otóż popełniliśmy bardzo prosty błąd, który łatwo popełnić pisząc takie funkcje. Nie przewidzieliśmy nigdzie, że inne piny (oprócz 13) mogą być wyjściami. Sprawdź sekcje setup:

Możemy to naprawić stosunkowo prosto, wystarczy przenieść konfigurację pinów do naszej funkcji. Wyglądałoby to następująco:

Teraz wszystko działa, jednak muszę przyznać, że nie jest to eleganckie rozwiązanie. Dlatego odradzam używania w bardziej profesjonalnych projektach tego typu rozwiązań. Jednak na etapie hobbystycznym nie powinno się nic złego stać i możemy sobie ułatwiać życie taką prostą funkcją.

Pora na ostatnią informację o funkcjach zanim przejdziemy do czujnika odległości.

Funkcje zwracające wynik

Do tej pory wykorzystywaliśmy tylko funkcje, które robiły pewne operacje, ale nie musiały zwracać żadnego wyniku. Zadaniem nowej funkcji będzie... obliczenie pola kwadratu o zadanym boku i zwrócenie wyniku (obliczonego pola). Na początek konieczna jest deklaracja funkcji, tym razem nagłówek będzie wyglądał tak:

Jak widać, oprócz argumentu (bok kwadratu) przed funkcją pojawił się int. Oznacza to, że zwróci ona wynik będący liczbą. W tym przypadku będzie to oczywiście obliczone pole.

Wnętrze funkcji powinno wyglądać natomiast tak:

Podświetliłem tutaj linię, ze słowem return. Po nim umieszczamy wartość (zmienną), która jest zwracana jako wynik tej funkcji. Czyli dla argumentu 4, w wyniku działania funkcji powinniśmy otrzymać 16,  bo 4*4 = 16. Zastanawiasz się pewnie, co to znaczy, że funkcja zwraca wynik. Gdzie trafia ta wartość?

Sprawdźmy poniższy przykład:

Uruchom przykład. W monitorze portu szeregowego powinien pojawiać się wynik operacji, czyli 16. To, że funkcja zwraca wynik oznacza, że po wykonaniu w miejscu jej wywołania "podstawiony zostanie wynik". W tym wypadku zamiast  kwadratPole(4); podstawiane jest 16, które zapisywane jest do zmiennej wynik.

Deklaracje osobnej zmiennej jest jednak zbędna. Równie poprawnie (ale trochę mniej czytelnie) sprawdzi się poniższy zapis:

Do pełni szczęścia brakuje tylko, aby bok kwadratu mógł być wysyłany do Arduino z komputera. Wykorzystamy tutaj wiadomości z części kursu o komunikacji przez UART. Program będzie wyglądał następująco:

Nowością jest jednak zapis  odebraneDane.toInt(). Jak pewnie pamiętasz, podczas komunikacji przez UART wszelkie znaki przesyłane są w formie kodów ASCII. Stąd podczas naszych testów nie mogliśmy w łatwy sposób wysyłać do Arduino liczb.

Można to jednak bardzo łatwo zmienić. Wystarczy do doczytanego ciągu znaków (stringa) dopisać .toInt(). Dzięki temu liczba przesłana jako tekst zostanie przekonwertowana na liczbę typu int.

Zadanie domowe 9.1

Napisz funkcje, które obliczają pola prostokąta, koła oraz trójkąta. Wyniki wyślij do PC przez UART!

Ultradźwiękowy czujnik odległości HC-SR04

Pora na opis czujnika odległości, na które czekało wiele osób. Czujnik HC-SR04 składa się z nadajnika i odbiornika ultradźwięków oraz kilku układów scalonych. Dzięki nim, na podstawie nasłuchiwania, jak odbija się sygnał akustyczny (niesłyszalny dla człowieka) możemy całkiem precyzyjnie określić odległość czujnika od przeszkody. Zupełnie jak... nietoperze:

Nietoperz fot. Wikipedia.

Nietoperz fot. Wikipedia.

Wróćmy jednak do Arduino i czujnika odległości. Na początek skupmy się na jego czterech pinach sygnałowych. Dwa z nich służą do zasilania układu (Vcc, GND), a drugie dwa (trigger i echo) do wykonywania pomiarów.

Trigger, to wejście wyzwalające. Gdy podamy na nie stan wysoki (przez min. 10 mikrosekund), to rozpocznie się pomiar odległości. Natomiast z wyjścia echo odczytamy zmierzoną odległość. Maksymalny zasięg tego niepozornego układu deklarowany jest przez producenta na 4 m.

Pora na pierwszy, prosty przykład wykorzystania czujnika w praktyce.

Miernik odległości

Podłącz stworzymy układ, który będzie w regularnych odstępach czasu dokonywał pomiarów odległości i będzie wyświetlał je na ekranie komputera. W tym celu konieczne jest złożenie układu zgodnie z poniższym schematem:

Ard_9_11

Opis wyprowadzeń znajduje się fizycznie na czujniku, jednak dla formalności przedstawiam go poniżej. Patrząc od góry  (tak, jak na powyższym schemacie) i od lewej:

  1. Vcc - 5V
  2. Trig
  3. Echo
  4. Gnd

Zdjęcie złożonego układu umieszczam poniżej. Wybaczcie jeden nadmiarowy przewód (zielony), który zapomniałem odpiąć na czas robienia fotografii:

Podłączenie czujnika HC-SR04 do Arduino.

Podłączenie czujnika HC-SR04 do Arduino.

W naszym przypadku wejście trig łączymy z pinem nr 12, a echo z pinem nr 11. Aby nam się to nie myliło proponuję od razu zrobić odpowiednie definicje:

Wiesz mniej więcej jak wyzwolić pomiar odległości. Czas zapisać to w formie programu:

Zwróć uwagę na nową funkcję  delayMicroseconds();, jest to nic innego jak odpowiednik dobrze nam znanej funkcji  delay();. Różnica jest chyba oczywista. Nowa funkcja odlicza czas w mikrosekundach. Przypomnę tylko, że używana do tej pory funkcja delay odmierza milisekundy.

Sekwencja uruchamiająca pomiar jest bardzo prosta. Na początku ustawiamy na pinie połączonym z wejściem wyzwalającym czujnika sygnał niski. Wystarczą 2 mikrosekundy, następnie ustawiamy stan wysoki na 10 mikrosekund. Czujnik wykonuje pomiar i zwraca wyniki na pinie echo.

Pytanie jak go odczytać? Czy jest do tego potrzebny UART lub inny interfejs komunikacyjny? Nie, na szczęście czujnik ten jest bardzo prosty i zmierzona odległość reprezentowana jest przez impuls (stan wysoki) na pinie echo. Jego długość jest proporcjonalna do odległości. Im jest on dłuższy, tym zmierzona odległość jest większa. Tutaj przyda się nam nowa funkcja dostępna w Arduino.

Pomiar czasu trwania impulsu w Arduino

Na szczęście w Arduino zaimplementowano bardzo prostą funkcję, która potrafi zmierzyć czas trwania impulsu podanego na dowolne wejście. Jego długość musi się mieścić w przedziale od 10 mikrosekund do 3 minut. Start pomiaru następuje w momencie wykrycia zmiany stanu na pinie.

Jej składania jest bardzo prosta:

Funkcja, w najprostszej postaci przyjmuje tylko dwa argumenty. Numer pinu, który ma zostać sprawdzony oraz poziom logiczny (niski/wysoki), który ma być mierzony.

Czyli w celu zmierzenia odległości musimy wykonać następującą operację:

Uruchomienie takie programu sprawi, że na ekranie zaczną pojawiać się liczby. Im postawimy przeszkodę bliżej czujnika tym będą one mniejsze. Jednak daleko im do używanych przez nas jednostek. Aby pomiar był czytelny dla człowieka wynik musimy podzielić przez "magiczną liczbę".

Sprawdź jak działa poniższy program - teraz wynik powinny wyświetlać się w centymetrach:

Oczywiście wartość, przez którą dzielimy (58) nie jest "magiczna". Wynika ona z czasu przez jaki dźwięk pokonuje odległość 1 cm oraz wzoru na odległość jaką przedstawił producent.

Funkcja zwracająca odległość czujnika w cm

Jak widać fragment programu, który pozwala na uzyskanie pomiaru jest stosunkowo długi. Bardzo wygodnie byłoby, gdybyśmy dysponowali funkcją, która robi to wszystko. Napiszmy ją. Nie ma na co czekać, przedstawiam gotowy fragment, w końcu to tylko kopiowanie powyższego kodu:

Teraz w odpowiednim miejscu programu wystarczy wywołać funkcję:

Znacznie czytelniej, prawda?

Sprawdzanie zakresu odległości

Pora na ostatnie ćwiczenie z tej części kursu Arduino. Tym razem dodajmy do naszego układu buzzer, czyli element wydający dźwięk. Wygląda on tak:

Element ten ma dwa wejścia, gdy na jedno z nich podamy Vcc, a drugie połączymy z masą, to... Buzzer zacznie wydawać głośny dźwięk. Nie jest on zbyt przyjemny, więc proponuję pozostawić na nim naklejkę (dzięki temu wydawany odgłos jest cichszy). Jak łatwo domyśleć się po napisie etykieta ta ochrania wnętrze buzzera podczas mycia płytek drukowanych. Robi się, to głónie w celu usunięcia śladów po lutowaniu.

W celu wykorzystania buzzera podłączamy go do Arduino zgodnie z poniższym schematem.

Schemat montażowy - buzzer i Arduino.

Schemat montażowy - buzzer i Arduino.

Symbol użyty przeze mnie do narysowania schematu montażowego odbiega trochę od wyglądu buzzera, więc można się upewnić dodatkowo na zdjęciu mojego układu:

Zdjęcie rzeczywistego układu.

Zdjęcie rzeczywistego układu.

Tym razem napiszemy funkcję, której zadaniem będzie sprawdzanie, czy obiekt znajduje się w określonej odległości od czujnika. Jeśli tak się stanie, to włączy się alarm (dźwięk buzzera).

Funkcja sprawdzająca zakres będzie korzystała z poprzedniej funkcji zmierzOdleglosc(),

Jako argumenty funkcji podajemy dwie liczby całkowite. Następnie sprawdzamy czy zmierzona odległość jest większa od początkowej wartości naszego przedziału (a) oraz mniejsza od maksymalnej wartości (b).

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

Sprawdź jak program działa w praktyce. Moim zdaniem jest to idealny zalążek do budowy prostej centralki alarmowej, która będzie pilnowała Twoich cennych skarbów!

Zadanie domowe 9.3

Napisz program, który przedstawia odległość przeszkody od czujnika na linijce diod LED. Im przeszkoda jest bliżej czujnika, tym więcej diod powinno się świecić. Przykładowa linijka może być zbudowana jak poniższa:

Początek budowy linijki LED.

Początek budowy linijki LED.

Zadanie domowe 9.4

Sprawdź jak zmienia się odczyt czujnika w zależności od materiału, z którego zbudowana jest przeszkoda. Czy widzisz różnice w odległości wskazywanej, gdy czujnik kierowany jest np.: na twarde lub miękkie przedmioty? Porównaj takie przeszkody jak ściana, koc kartka papieru.

Podsumowanie kursu Arduino!

Zupełnie nie wiem jak to się stało, ale właśnie poznałeś cały materiał, który był przewidziany na kurs Arduino dla początkujących! Mam nadzieję, że nauka była dla Was owocna. Jeśli będziecie zainteresowani, to obiecuję dodać jeszcze jedną część ekstra oraz kontynuować prace nad kursem dla średniozaawansowanych (kolejne 9 nowych odcinków).

Nawigacja kursu

Nie chcecie przeoczyć kolejnych odcinków? Korzystając z poniższego formularza możecie zapisać się na powiadomienia o nowych publikacjach!

Damian (Treker) Szymański

Arduino, buzzer, czujnik, funkcje, kursArduino

Komentarze

Komentarze do tego wpisu są dostępne na forum: