Czujnik parkowania z wykorzystaniem taniego modułu HC-SR04

img_5553Witajcie ponownie! Dziś na warsztat postanowiłem wziąć trochę kolorowych diod, tani moduł ultradźwiękowego czujnika odległości HC-SR04 i zrobić prosty czujnik, który znacznie pomoże przy parkowaniu w garażu w szczególności tym, których samochody mieszczą się w nim na styk.

Co będzie nam potrzebne?

  • Teensy, Arduino (może być jego klon) lub inny mikrokontroler (w przykładzie będziemy używali Teensy)
  • 6 zielonych diod + rezystory 75 Ohm do każdej,
  • 3 żółte diody + rezystory 110 Ohm do każdej,
  • 2 czerwone diody + rezystory 220 Ohm do każdej,
  • Czujnik ultradźwiękowy np. HC-SR04.

Zasada działania czujnika

Czujnik posiada 2 piny: trigger oraz echo. Pierwszym z nich pobudzamy moduł do rozpoczęcia pomiaru, drugi informuje nas o wyniku pomiaru. Aby rozpocząć pomiar należy na pin trigger przez 10 mikrosekund podawać stan wysoki. W tym czasie czujnik rozpoczyna pomiar. Kiedy go skończy na pinie echo otrzymamy stan wysoki przez pewien czas t. Impuls ten jest tym dłuższy im dalej stoi obiekt od którego odbiła się fala ultradźwiękowa i reprezentuje on liczbę mikrosekund od wysłania fali do jej powrotu do czujnika. Stąd, aby otrzymać wynik w centymetrach wystarczy podzielić ten czas na 2, a następnie pomnożyć przez prędkość dźwięku w powietrzu równą 340 m/s (pamiętając o tym, że nasze t wyrażone jest w mikrosekundach). Może Was zastanawiać dlaczego na początku wykonujemy dzielenie przez 2. Już spieszę z wyjaśnieniem: jest to czas, w którym fala przebywa drogę od czujnika do przeszkody i z powrotem, stąd czas t odpowiada podwójnej odległości czujnika od przeszkody. Dzieląc wynik przez 2 niwelujemy ten efekt.  Z takimi informacjami i podstawową matematyczną wiedzą o przekształcaniu jednostek możemy sobie wyprowadzić ostateczny wzór:

10^2 * s [m] = v [m/s] *  (10^6 * t [s])

10^2 * s [m] = vt [m] * 10^6 | : 10^2

s [cm] = vt [cm] * 10^4

Widzimy teraz, że przekształcając wynik na cm prawa strona naszego równania jest 10000 razy większa niż powinna. Aby jednostki się zgadzały dzielimy prawą stronę przez ten nadmiar i uzyskujemy ostateczny wzór, który po podstawieniu za v wartości 340 m/s oraz podzieleniu t przez 2 wygląda tak:

s [cm] = (340 * (t/2)) / 10000

Huh, wygląda to na dość skomplikowane prawda? Komu chciałoby się to robić? Na szczęście z pomocą przychodzi biblioteka NewPing.h, która udostępnia nam wysokopoziomowe metody do obsługi różnych czujników ultradźwiękowych.
1g1ssp

Podłączenie

Schemat połączeń jest banalnie prosty. Najpierw podłączamy zielone diody do pinów od 0 do 5, żółte do pinów 6-8 oraz czerwone do pinów 9 i 10. Do pinów 11 i 12 podłączamy kolejno echo i trigger z czujnika ultradźwiękowego. Powinno to wyglądać tak:

schemat

Kod

Kod jak zwykle można pobrać tutaj.

Omówmy kilka fragmentów zaczynając od

#include <NewPing.h>

#define TRIGGER_PIN 12
#define ECHO_PIN 11
#define MAX_DISTANCE 200
 
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);

Załączamy tutaj bibliotekę NewPing, ustawiamy stałe takie jak numery pinów trigger i echo oraz maksymalna odległość jaką jest w stanie zmierzyć czujnik (jeśli czas echo będzie dłuższy niż powinien dla maksymalnej odległości biblioteka przerwie oczekiwanie na wynik i zwróci 0).

void setup() {
  for(int x = 0; x < 11; x++) {
    pinMode(x, OUTPUT);
    digitalWrite(x, HIGH);
    delay(50);
  }

  for(int x = 10; x >= 0; x--) {
    digitalWrite(x, LOW);
    delay(50);
  }
}

W funkcji setup nie dzieje się nic specjalnego. Za pomocą dwóch pętli ustawiamy piny z diodami jako wyjścia i wykonujemy prostą animację. Dzięki temu, że podłączyliśmy diody do kolejnych pinów od 0 do 10 łatwo możemy przekształcić zmierzony dystans w liczbę diod do zapalenia.

void loop() {
  unsigned int distance = sonar.ping_median(5);
  distance = sonar.convert_cm(distance);
  setLeds(distance, 10, 100);
}

W funkcji loop odczytujemy medianę z 5 pomiarów odległości (dla ustabilizowania wyników i odrzucenia błędnych pomiarów), następnie konwertujemy czas na centymetry i wywołujemy funkcję setLeds.

void setLeds(unsigned int distance, unsigned int minDist, unsigned int maxDist) {
  int leds;
  static bool isTooFar = true;
  
  leds = map(distance, minDist, maxDist, 9, 0);
  if(distance <= minDist) leds = 10;
  if(distance == 0 && isTooFar) leds = 0;
  
  for(int x = 0; x < 11; x++) {
    digitalWrite(x, x <= leds);
  }
  
  if(distance > 0) {
    if(distance >= maxDist)
      isTooFar = true;
    else
      isTooFar = false;
  }
}

Omówmy teraz co dzieje się w funkcji, która ustawia stan diod. Przyjmuje ona 3 parametry:

  • distance – zmierzona odległość od przeszkody,
  • minDist – odległość, przy jakiej zapalą się wszystkie lampki,
  • maxDist – odległość, przy jakiej zgasną wszystkie lampki.

Zmienna leds będzie reprezentowała ile diod ma się zaświecić dla zadanego distance, natomiast statyczna (taka, która nie usunie się z pamięci po wyjściu z funkcji) zmienna isTooFar powie nam, czy przeszkoda jest za daleko i czujnik jej nie widzi, czy może jest zbyt blisko. Będziemy potrzebowali takiego rozróżnienia, aby w przypadku gdy nie ma żadnej przeszkody w polu widzenia czujnika nie świeciły się wszystkie diody.

Za pomocą funkcji map przekształcamy distance z zakresu od minDist do maxDist na wartości od 9 do 0 (im mniejsza odległość tym więcej diod). Linijkę niżej ustawiamy 10 diod, jeżeli distance jest mniejszy od minDist. Kolejnym if’em sprawdzamy, czy w przypadku jeśli distance jest równy 0 jest to spowodowane tym, że przeszkoda jest za blisko czy może za daleko.

Pętla for iteruje po wszystkich diodach i zapala tylko te, które są na pinach mniejszych, bądź równych zmiennej leds.

Na samym końcu funkcji sprawdzamy, czy pomiar się udał, jeżeli tak to sprawdzamy czy odległość jest większa od maxDist. Jeśli tak to wiemy, że przeszkoda na pewno nie jest zbyt blisko czujnika i możemy założyć, że niedługo zniknie z pola widzenia (ustawiamy zmienną isTooFar na true).

Co dalej?

Tak skonstruowany układ możemy zamknąć w jakiejś obudowie, diody wyprowadzić na kabelku i zamontować sobie całość na ścianie w garażu tak, aby czujnik był na wysokości zderzaka, a LEDy były dobrze widoczne zza szyby samochodu :)

Poniżej zdjęcia jak układ prezentuje się w akcji:

img_5549 img_5550

img_5558