niedziela, 13 stycznia 2019

Parkowanie w garażu - Laserowa bariera optyczna & Ultradźwiękowy miernik odległości na Digispark ATtiny85

W normalnych warunkach takie rozwiązania nie są konieczne, jeśli ma się samochód z czujnikami parkowania i względnie duże miejsce parkingowe w garażu. To niestety mnie nie dotyczy. Mam starszy już samochód i ciasny garaż wypchany gratami, na które wolałbym przypadkiem nie wjechać. Z tego powodu zacząłem się rozglądać za rozwiązaniami, które by ułatwiły mi życie i zredukowały do zera nerwy związane z parkowaniem.

Prototypowy samochód ;)
Założenia

Parkowanie wspomagają dwa niezależne układy monitorujące aktualne położenie samochodu. Zasilane są z baterii. Obwód zasilania obu układów zamykany jest w momencie całkowitego otwarcia bramy. Wstępnie użyję tu kontaktron (normalnie otwarty) lub coś w stylu wyłącznika krańcowego. W bardziej wypasionej wersji chciałbym dodać włączanie zasilania przekaźnikiem sterowanym drogą radiową (433MHz) z pilota od garażu pod jeden z niepodpiętych/wolnych przycisków.

Pierwszy z układów (bariera optyczna), zamontowany przy wjeździe, będzie sygnalizował dźwiękiem znalezienie się tyłu samochodu w garażu. Drugi układ (miernik odległości), zamontowany w głębi, będzie monitorował (linijka LEDowa)  przód pojazdu. Taki rozkład czujników oznacza wjeżdżanie przodem do garażu.


Laserowa bariera optyczna

Zadaniem bariery jest sygnalizowanie znalezienia się tyłu samochodu w garażu. Chodzi o to, aby tył znajdował się jak najbliżej bramy wjazdowej (1 - 2 cm) i tym samym była największa możliwa przestrzeń przed samochodem (dostęp do półek i swobodne przemieszczanie się).


Bariera w działaniu

Wykorzystałem tu dwa niezależnie kupione moduły lasera i prostego czujnika optycznego z fotorezystorem. Jego oświetlenie laserem powoduje zaświecenie się diody LED i uruchomienie brzęczyka piezo. Okrojona przeze mnie nazwa czujnika optycznego ze strony sprzedawcy brzmi "DIY Kit Photosensitive Acousto-optic Alarm". Z kolei moduł lasera można znaleźć pod hasłem "KY-008 650nm Laser Module 5V 5mW Red Laser". Schemat bariery przedstawia z grubsza obrazek poniżej.


Schemat blokowy bariery optycznej
Moduł lasera zasilany jest przez zasilacz, ew. baterie ze stabilizatorem dającym napięcie 5V. Dodatkowo dodałem wyłącznik z racji na oszczędność prądu. Przy zasilaniu 4,5V  (3x1,5V AA) zużycie prądu wynosiło 22,2mA. Po uruchomieniu modułu lasera, wiązka światła kierowana jest na detektor, którym jest fotorezystor. W efekcie uruchomiony zostaje brzęczyk piezzo.

Moduł czujnika zasilany jest dwoma bateriami AA (3V) tym razem bez wyłącznika. Tu pobór prądu jest niewielki. Dodatkowo wymontowałem diodę LED, która jest zbędna. Przy odległości między laserem a fotorezystorem 15cm pobór prądu wyniósł 30mA dla stanu aktywnego (czyli laser oświetla fotorezystor) i zmalał do poziomu kilku mikroamperów przy braku oświetlenia. Takie też warunki będą panować w garażu. Układ czujnika posiada rezystor montażowy do regulacji czułości na oświetlenie.  Czułość ustawiłem na minimum (prawie maksymalny obrót rezystora zgodnie ze wskazówkami zegara). Jednak niecałkowicie do końca, bo czujnik przestał mi reagować nawet na laser. W warunkach polowych (pomimo ustawienia na minimum czułości) warto zamknąć czujnik w nieprzezroczystej obudowie z niewielkim otworem przez który będzie wpadać światło lasera. Sam fotorezystor zamknąć w czarnej tubie np. z koszulki termokurczliwej. To pozwoli uniknąć sytuacji w której inne źródła światła (np promieniowanie słoneczne) uruchomią brzęczyk piezo.


Ultradźwiękowy miernik odległości

Zadaniem miernika jest monitorowanie odległości przodu pojazdu od półek z gratami i sygnalizacja świetlna informująca o odległości. Wykorzystałem tu pierścień złożony z 16 LEDów RGB 5050 ze sterownikiem WS2812. Za pomiar odległości odpowiada czujnik HC-SR04, a całością steruje Digispark ATtiny85.


Układ prototypowy
Wybrałem tę platformę z racji na kompaktowe wymiary i chęć użycia po raz pierwszy czegoś z rodziny ATtiny ;) Do uruchomienia układu wystarczyły mi trzy piny - jeden do sterowania LEDami i kolejne dwa do obsługi HC-SR04. W przypadku braku obiektu miga jedna dioda LED na zielono - można to nazwać stanem czuwania. Jeśli obiekt będzie w zasięgu czujnika zapalają się kolejne LEDy, tym więcej, im bliżej  jest on czujnika. Dodatkowo zmieniają się kolory (tu z grubsza nawiązuje do sygnalizacji drogowej) od zieleni poprzez kolor żółty i pomarańczowy aż do czerwieni. LEDy są zapalane liniowo do odległości obiektu od czujnika, przy czym jeśli jest to mniejsza odległość niż 30cm, trzy ostatnie LEDy zapalane są jednocześnie na czerwono. Dodatkowo, jeśli w ciągu kolejnych pięciu sekund, wykryta zmiana odległości będzie mniejsza od 5cm, to miernik także przechodzi w stan czuwania (miga jedna zielona dioda LED).




Digispark ATtiny85 jest niestety dość problematyczny, jeśli chodzi o możliwość jego zaprogramowania. Zdarzało się, że nie po każdym podłączeniu go do portu USB jest wykrywany przez Windowsa (mam 8kę). Nie ma też mowy o podłączaniu czegokolwiek do portów 3 i 4, przez które właśnie odbywa się komunikacja po USB. Pech chciał, że akurat na tych pinach pracował mi HC-SR04, czego też nie odkryłem od razu. Stanęło na tym, że aby zaprogramować ATtiny musiałem odłączać HC-SR04. Są jeszcze inne problemy. Przy zajętych portach od USB, nie działa komunikacja szeregowa, która przydaje się do debugowania. Zresztą i tak, przy dodanej bibliotece od obsługi USB zdarzyło mi się przekroczyć dostępną pamięć. Do prostego debugowania (np. sprawdzenia flow'u programu i wejść w poszczególne if-y) wystarczyła mi sygnalizacja oparta na LEDach.


Schemat blokowy czujnika odległości
Poniżej kod sterujący całością. Do obsługi HC-SR04 użyłem biblioteki NewPing. Kod nie jest specjalnie rozbudowany, a to co można konfigurować starałem się wynieść do stałych o "wymownych nazwach" na początku pliku. Pomiar odbywa się w zakresie 0 - 100cm (f-cja inMeterRange(int distance)), powyżej tego zakresu miernik jest w stanie czuwania (f-cja outOfMeterRange()). Do stanu czuwania miernik przechodzi także po pewnym czasie bezczynności określonym stałą NO_ACTIVITY_TIMEOUT (tu 5s). Bezczynność oznacza brak zmian odległości mniejszą niż 5cm.

#include <NewPing.h>
#include <Adafruit_NeoPixel.h>

//NeoPixels config
#define PIN 0
#define NUMPIXELS 16

//LCD config (R,G,B)
#define GREEN  0,255,0
#define YELLOW 255,255,0
#define ORANGE 255,69,0
#define RED    255,0,0
#define GREEN_THRESHOLD  9
#define YELLOW_THRESHOLD 5
#define ORANGE_THRESHOLD 2
#define NO_ACTIVITY_TIMEOUT 5000  //in miliseconds

//NewPing config
#define TRIGGER_PIN  3
#define ECHO_PIN     4
#define DISTANCE_MAX 100
#define DISTANCE_MIN 30

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
NewPing sonar(TRIGGER_PIN, ECHO_PIN, DISTANCE_MAX);
unsigned long lastPositionTime = 0;
uint8_t distance = 0;
uint8_t lastDistance = 0;
uint8_t forceStandby = 0;

void setup()
{
    pixels.begin();
}

void loop()
{
  distance = sonar.ping_cm();
  
  if (abs(distance - lastDistance) > 5) {
    lastDistance = distance;
    lastPositionTime = millis();
    forceStandby = 0;
  } else if (millis() - lastPositionTime > NO_ACTIVITY_TIMEOUT) {
    //force one blinking LED
    forceStandby = 1;
  }

  if (
    distance > DISTANCE_MAX 
    || 0 == distance 
    || 1 == forceStandby
  ) {
    outOfMeterRange();
  } else {
    inMeterRange(distance);
  }
}

void outOfMeterRange()
{
    pixels.setPixelColor(NUMPIXELS - 1, pixels.Color(GREEN));
    pixels.show();
    delay(500);
    pixels.clear();
    pixels.show();
    delay(500);
}

void inMeterRange(int distance)
{
    int countTo;

    if (distance > DISTANCE_MIN && distance < DISTANCE_MAX) {
      countTo = map(distance, 0, 100, 0, 15);
    } else {
      countTo = 0;
    }
    
    for (int i = NUMPIXELS - 1; i >= countTo; i--) {
        if (i > GREEN_THRESHOLD) {
          pixels.setPixelColor(i, pixels.Color(GREEN));
        } else if (i > YELLOW_THRESHOLD) {
          pixels.setPixelColor(i, pixels.Color(YELLOW));
        } else if (i > ORANGE_THRESHOLD) {
          pixels.setPixelColor(i, pixels.Color(ORANGE));
        } else {
          pixels.setPixelColor(i, pixels.Color(RED));
        }
        
        pixels.show();
        delay(getNextLedDelay(countTo));
    }
    
    delay(200);
    pixels.clear();
    pixels.show();
    delay(500);
}

int getNextLedDelay(int counter)
{
  return 20 + counter * 2;
}


Powyższy kod jest także dostępny w repo HC_SR04_Distance_Meter. Po jego wgraniu pozostało 40% wolnej pamięci (Sketch uses 3644 bytes (60%) of program storage space. Maximum is 6012 bytes. Global variables use 63 bytes of dynamic memory.). W przypadku zasilania z baterii, można by to miejsce zagospodarować na kod monitorujący napięcie zasilania i sygnalizujący niski poziom na użytej sygnalizacji LEDowej.

Źródła

Brak komentarzy:

Prześlij komentarz