Powolutku… czujnik odległości

Poznajemy ultradźwiękowy czujnik odległości – układ HC-SR04. Emituje on fale dźwiękowe o częstotliwości 40 kHz (czyli spoza zakresu słyszalności człowieka) i czeka na jej powrót. Mierząc czas nadejścia tej informacji (powrotnej) znamy odległość do obiektu, od którego odbiła się fala dźwiękowa (a właściwie 2x odległość, bo fala pokonała drogę w jedną stronę i z powrotem).

PINOUT: czyli jak podłączyć układ. VCC=5V.

Działanie układu odbywa się zgodnie z następującym protokołem (schematem):

  • włączamy TRIG (stan wysoki =5V) na 10 us aby uruchomić procedurę wyemitowania dźwięku (8 impulsów 40 kHz);
  • wyłączamy TRIG (stan niski =0V), pin ECHO przechodzi w stan wysoki i zmieni go na niski, gdy sygnał wróci (a jeśli nie wróci, ECHO zmieni się po czasie 38 ms);
  • mierzymy czas trwania sygnału wysokiego na pinie ECHO – znając ten czas i zakładając prędkość dźwięku 340 m/s mamy już odległość do obiektu.
Zasada działania czujnika – polecam stronę LAST MINUTE ENGINERING.

Pierwszy programik wykorzystuje funkcję pulseIn() w celu określenia długości trwania sygnału HIGH na pinie ECHO.

#define TRIG 8
#define ECHO 7

unsigned int t2;

void setup() {
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
  Serial.begin(9600);
}

void loop() {
    digitalWrite(TRIG, 0);
    
    delayMicroseconds(5);  
    digitalWrite(TRIG, 1);
    delayMicroseconds(10);  
    digitalWrite(TRIG, 0);

    //z wykorzystaniem funkcji pulseIn
    t2=pulseIn(ECHO, HIGH);
  
    Serial.print(t2);
    Serial.print(",  ");
    Serial.print(t2/58.0);    
    Serial.println(" [cm]");
  
    delay(100);
}

Nie jestem wielkim zwolennikiem funkcji pulseIn() – choć działa bardzo dobrze. Do odczytu stanu pinów służy funkcja digitalRead(), którą trzeba tylko odpowiednio „zapętlić” — poniższy kod właśnie to realizuje. Wykorzystujemy też funkcję mierzenia czasu działania Arduino w mikrosekundach (czyli w 1/1000 milisekund!) czyli funkcji micros(). Oto i kod:

#define TRIG 8
#define ECHO 7

unsigned int t1,t2;
byte aaa;

void setup() {
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
  Serial.begin(9600);
}

void loop() {
    digitalWrite(TRIG, 0);
    
    delayMicroseconds(5);  
    digitalWrite(TRIG, 1);
    delayMicroseconds(10);  
    digitalWrite(TRIG, 0);

    //czekanie na przelaczenie pinu ECHO w stan HIGH... wszak nic nie dzieje sie natychmiast!
    while (digitalRead(ECHO) == LOW);
    t1=micros();
   
    while ((aaa=digitalRead(ECHO)) == HIGH);// Serial.println(aaa);
    t2=micros()-t1;
  
    Serial.print(t2);
    Serial.print(",  ");
    Serial.print(t2/58.0);    
    Serial.println(" [cm]");
  
    delay(100);
}

Dwie rzeczy wymagają wyjaśnienia: 1) Pętle while kończą się średnikiem – czyli pętla nie wykonuje nic! od razu wraca do sprawdzenia warunku kontynuowania pętli – tak szybko, jak się da; nie ma strat czasu na wypisywanie rzeczy, na jakieś obliczenia…. 2) czekamy na ustawienie pinu ECHO w stan HIGH tak długo, jak czujnik SR_HC04 to zrobi (bo choć protokół o tym właśnie mówi, to nic nie dzieje się natychmiast – nawet światło ma skończoną prędkość); dlatego pojawiła się pętla trwająca tak długo, aż stan pinu ECHO jest LOW. Po tej pętli mamy pewność, że ECHO jest już HIGH i uruchamiamy „stoper” (zapisujemy aktualną wartość mikrosekund) do zmiennej t1. Teraz ponownie zapętlamy program aż stan pinu ECHO zmieni się na LOW – to sygnalizuje powrót sygnału do układu. Ta pętla posługuje się pomocniczą zmienną aaa (ale beznadziejna nazwa) zapisującą stan pinu ECHO – niby niepotrzebnie, ale można odkomentować następną linię i będziemy widzieć jej wartość. To kolejna mała rzecz wymagająca komentarza. Po tej pętli ponownie zapisujemy „stoper” – czyli aktualną wartość mikrosekund, tworząc zmienną t2 równą różnicy zatrzymanego czasu i wartości t1… Zmienna t2 ma teraz dokładnie takie samo znaczenie jak w pierwszym przykładzie (programie) i dalsza część pozostaje niezmieniona.

Które rozwiązanie lepsze? W przypadku jednego czujnika pewnie pierwsze jest fajniejsze, ale gdy mamy więcej czujek działających jednocześnie? Wówczas rozwiązanie z pulseIn() nie zagra, bo zatrzyma wykonanie programu i nie pozwoli na wykrycie sygnału wracającego do innego czujnika – czyli popsuje wyniki dla drugiego, trzeciego czujnika. Drugie rozwiązanie można łatwo rozbudować do współpracy z wieloma czujnikami (np. wprowadzając zmienne t2, t3, t4… ) mierzące czas powrotu sygnału z kolejnych czujek. Ale nie upieram się nad wyższością jednego nad drugim. Mi bardziej pasuje funkcja digitalRead() i dlatego wolę drugi programik. Warto na koniec wspomnieć, że jest dedykowana biblioteka do obsługi układu SR-HC04 (gdzie? poszukaj!) i wiem, że świetnie nadaje się do obsługi wielu czujników.

(c) K.G. 2021

Zaczynamy! … od zera…

Ustaliliśmy, że spotykamy się w piątki o 12:15 (ale mamy czas tylko do 13:30). Zaczynamy od zera – czyli wyjaśnień, jak działa LED i do czego jest potrzebny rezystor w obwodzie. Znaczy nie wyjaśniłem jak działa LED ale omówiłem jego funkcję w obwodzie elektrycznym. No dobrze, Panowie wiedzieli to i owo, ale te informacje należało podać i wyjaśnić wątpliwości (a były takie! z istotniejszych: czy kolejność opornika i LEDa ma znaczenie? NIE!). Jeszcze bez podłączenia Arduino sprawdziliśmy działanie układu zasilanego akumulatorem żelowym 6V, mierzyliśmy prąd, napięcia… nuda? Może. Ale niezbędna. Sprawdzialiśmy, jak świeci LED z oporikiem 10 000 omów, 470 omów a nawet jak z 22 omami (info na końcu wpisu). Dopiero potem podłączyliśmy płytkę Arduino UNO – dowiedzieliśmy się, co to są piny GND, 3.3V oraz 5V. Sprawdziliśmy multimetrem napięcie (a jakże!). A potem podłączyliśmy LED-a do 5V. Działa!

Arduino jako źródło napięcia 5V. Układ z LED-em.

OK, ale Arduino się programuje… Więc uruchomiliśmy darmowe Arduino IDE i napisaliśmy pierwszy programik – świecący co 5s LEDem podłączonym do pinu cyffrowego numer 7 – dlaczego akurat 7? a dlaczego nie 😉 Mamy do wyboru 14 sztuk pinów cyfrowych (numerowane od 0 do 13), to któryś trzeba było wybrać.

Programujemy Arduino!

Program wymagał wyjaśnienia, gdzie jest funkcja main() z języka C/C++, co robią funkcje setup() oraz loop() a także co to są pinMode(), digitalWrite() oraz delay(). Mam nadzieję, że było w miarę jasno (choć oczywiście – pytania w głowie buszują, z czasem znajdą się na nie odpowiedzi….).

Nasz pierwszy programik… Czas w funkcji delay() podaje się w ms.

Następnie bawiliśmy się w zmianę czasu trwania świecenia/nieświecenia LED-a. Ustawiliśmy w funkcjach delay() czas 1000 s (czyli 1 s), potem 100 ms a nawet 10 ms (czyli 100x na sekundę!). Oczywiście w tym ostatnim przypadku nie udało się zaobserwować błysków, a jedynie ciągłe świecenie. Przy tej okazji ponowne wróciliśmy do multimetru i sprawdziliśmy, jaki pomiar wskaże urządzenia. Dowiedzieliśmy się co-nieco o uśrednianiu pomiarów przez multimetr.

Ale ta zabawa w zmianę czasu doprowadziła nas do sprawdzenia zdolności percepcji człowieka – sprawdziliśmy, czy oko rejestruje zmiany częściej niż 24x na sekundę (chyba mamy tu jakiegoś amatora filmów!). Udało się. Nie wiesz o co chodzi? To zapraszam na koło w kolejny piątek 😉

A może dwa LEDy? Spróbowaliśmy świecić na przemian!

Świecenie na przemian LEDami podłączonymi do portów 7 i 3. Zwróć uwagę, że HIGH to 1, a LOW to 0.

Potem każdy ze studentów dostał w prezencie Arduino UNO…. wirtualnie 😉 zachęcam do ponowienia zabawy w TInkerkadzie.

I jeszcze jedno: chciałem spalić LED-a. Podałem opornik 22 omów do zasilania 6.3V i…. spodziewałem się szybkiego BUM! A tu przez ponad godzinę LED nie poddawał się, przyjmował okolo 138 mA prądu i świecił (no dobrze, zmienił barwę na pomarańczową – z żółtej – ale wytrzymał). Temperatura na rezystorze to nawet ponad 100C więc taki układ to zdecydowanie błąd w sztuce – ale o dziwo działał u nas przez godzinę (a może i dłużej, ale go rozłączyłem). Nie róbmy tak zbyt często – pamiętajmy, że LEDy „lubią” prąd do ~20mA, choć i tak polecam <10mA bo już wtedy świecą bardzo wyraźnie.

Próba szybkiego zniszczenia LED-a: nie udała się! przyjął 138 mA prądu na „klatę” i działa! (jest już uszkodzony, długo nie pociągnie). Rezystor rozgrzał się do 95 C (choć sonda nie dotyka go dobrze, jak ją poruszyć to widać nawet 110C).

Zachęcam do zastanowienia się nad powyższym układem: czy wskazania amperomierza się zgadzają z przewidywaniami teoretycznymi? Opornik w obwodzie to 22 ohm, zasilanie 6.3V. Śmiało przelicz sam! Coś się jakby nie zgadza? Może warto kliknąć w obrazek poniżej o poczytać trochę?

https://www.digikey.com/

(c) K.G. 2021