Zmierz prędkość i kierunek wiatru

Podłączenie i odczyt z czujnika wiatru

Prosta stacja pogodowa

W tym wpisie zajmiemy się podłączeniem oraz odczytaniem danych z czujnika prędkości oraz kierunku wiatru. Pozwoli nam to zbudować w niedalekiej przyszłości prostą stację pogodową, która będzie miała mierzyć prędkość oraz kierunek wiatru, sumę opadów, temperaturę, wilgotność oraz ciśnienie powietrza.

Zestaw czujników wykorzystany w tym projekcie

Początek

Zanim zaczniemy robić cokolwiek musimy się zapoznać z czym będziemy mieć do czynienia. Czujniki, które wykorzystamy w tym projekcie są zbudowane bardzo prosto. Czujnik wiatru oraz czujnik deszczu (w tym wpisie nie będziemy się zajmowali podłączeniem czujnika deszczu) działają na zasadzie zwarcia kontaktronu za każdym pełnym obrotem (w przypadku czujnika deszczu będzie to zwarcie kontaktronu po napełnieniu pojemnika na deszcz i przechyleniu się go w jedną ze stron). Czujnik kierunku wiatru to nic innego jak kilka rezystorów wraz z kontaktronami, których zwarcie spowoduje różną rezystancje w zależności od kierunku z którego wieje wiatr. Spójrzmy zatem do dokumentacji i zapoznajmy się najpierw z obwodem mierzącym kierunek wiatru.

Schemat czujnika kierunku wiatru

Jak widać na powyższym schemacie bez problemu można odczytać 8 kierunków z których wieje wiatr. W zależności który kontaktron się zewrze, układ będzie mieć odpowiednią wartość rezystancji. Jeśli chcecie sobie sprawdzić jak to wygląda w praktyce, a nie nie zabraliście się jeszcze za podłączenie czujników do Arduino, możecie użyć miernika uniwersalnego i sprawdzić rezystancję w zależności od kierunku

Schemat podłączenia

Patrząc dalej w dokumentację czujników, pokazany jest schemat jak odczytać wartość kierunku wiatru w zależności od rezystancji. Można również zauważyć, że w tabeli jest podane 16 wartości kierunku. Ma to związek z pośrednim położeniem czujnika kierunku. Na nasze potrzeby wykorzystaliśmy główne 8 kierunków, jeśli ktoś będzie chciał więcej kod można uzupełnić o odpowiednie wartości. W celu poprawnego odczytania wartości kierunku wiatru wykorzystamy port A0 Arduino, który będzie odczytywać wartość ADC. Naszym zadaniem będzie odczyt wartości z przedziału 0-1024 i przypisanie tych wartości do odpowiednich kierunków. Piny czujnika kierunku wiatru to dwa zewnętrzne we wtyczce RJ11 (pin 1 oraz pin 4). My na potrzeby projektu wtyczkę ucięliśmy i przylutowaliśmy listwę goldpin. W naszym przypadku czujnikowi kierunku wiatru odpowiadają kable koloru zielonego i czarnego. Dodatkowo zgodnie z dokumentacją dodaliśmy rezystor 10k. Na obudowie czujnika kierunku wiatru są zaznaczone 4 główne kierunki (północ, południe, wschód, zachód), dzięki czemu łatwiej sprawdzić jaka jest wartość rezystancji w zależności od kierunku.

Czujnik prędkości wiatru

Czujnik prędkości wiatru działa na zasadzie zwarcia kontaktronu co pełen obrót, tak wynika z dokumentacji i równa sie 1.492 MPH (mile/h) lub 2.4 km/h. Piny od czujnika prędkości wiatru to dwa środkowe we wtyczce RJ11 (pin 2 i pin 3). W naszym przypadku kable odpowiadające czujnikowi prędkości wiatru są koloru żółtego oraz czerwonego.

Tak jak napisaliśmy wcześniej, w dokumentacji zaznaczono, że za każdym pełnym obrotem następuje zwarcie kontaktronu. Przeglądając komentarze dotyczące produktu na stronie producenta, jeden z użytkowników napisał, że w jego przypadku na pełen obrót przypadają dwa zwarcia kontaktronu. Sprawdziliśmy i okazało się, że w naszym przypadku też tak jest. Jak to łatwo sprawdzić? Otóż za każdym razem kiedy kontaktron jest zwierany następuje w miarę słyszalne kliknięcie.

Czujnik deszczu

Czujnik deszczu to nic innego jak ruchomy pojemnik, który po napełnieniu przechyla się na jedną ze stron wylewając zebrany deszcz i powodując zwarcie kontaktronu. Według dokumentacji każde 0.2794 mm deszczu zebranego w czujniku deszczu spowoduje zwarcie kontaktronu. Na chwilę obecną czujnik deszczu jest nie podłączony w naszym projekcie.

Kod

Program składa się z dwóch części, pierwsza odpowiedzialna za mierzenie prędkości wiatru (kod został zapożyczony od osoby, która opisała swoje zmagania z czujnikiem prędkości i podała informację o dwóch zwarciach kontaktronu na obrót) oraz druga odpowiedzialna za mierzenie kierunku, z którego wieje wiatr.

Poniższy kod jest opatrzony w dużą liczbę komentarzy, także nawet początkujący powinni sobie dać z nim radę.

//Średnica anemometru w calach
float diameter = 2.75;
float mph; //Utworzenie zmiennej mile/godzinę
float kmh; //Utowrzenie zmiennej km/h

//Kierunek wiatru
int winddir = 0; //Utworzenie zmiennej wnddir (kierunek wiatru)
const byte WDIR = A0; // Przypisanie portu A0 jako portu odczytującego kierunek wiatru (jako stała)


// Odczyt obrotów (RPM)
volatile int half_revolutions = 0; //Utworzenie zmiennej połowa pełnego obrotu (half revolutions)
int rpm = 0; //Utworzenie zmiennej RPM (obroty)
unsigned long lastmillis = 0; //Utworzenie zmiennej long lastmilis

void rpm_fan() { //funkcja rpm_fan
  unsigned long static last_event = 0;
  if (millis() - last_event < 5) {   //debouncing
    return;
  }
  half_revolutions++; //zwiększ wartość zmiennej half_revolutions
}

int get_wind_direction() //Funkcja odczytująca kierunek wiatru
{
  unsigned int adc; //Utworzenie zmiennej adc

  adc = analogRead(WDIR); // Odczyt aktualnej wartości ADC

  //Jeżeli wartośc będzie się zawierała w podanym zakresie, zostanie zwrócony kierunek wiatru
  if ((adc > 760) && (adc < 799)) return (0);
  if ((adc > 450) && (adc < 470)) return (45);
  if ((adc > 85) && (adc < 100)) return (90);
  if ((adc > 170) && (adc < 199)) return (135);
  if ((adc > 270) && (adc < 299)) return (180);
  if ((adc > 610) && (adc < 640)) return (225);
  if ((adc > 930) && (adc < 960)) return (270);
  if ((adc > 870) && (adc < 899)) return (315);
  return (-1); //Jeśli jest jakiś błąd zostanie zwrócona wartość -1
}



void setup() {
  Serial.begin(9600); // Uruchomienie portu szeregowego z prędkością 9600
  pinMode(2, INPUT_PULLUP); // Aktywowanie rezystora podciągającego na pinie 2

  attachInterrupt(digitalPinToInterrupt(2), rpm_fan, FALLING);
  // Przypisanie funkcji rpm_fan przerwania zewnętrznego
  // Przerwanie zostanie wykonane jeśli stan zmieni się z wysokiego na niski (Falling)
}

void loop() {

  if (millis() - lastmillis >= 1000) {
    //Aktualizuj co sekundę, będzie to równoznaczne z odczytem częstotliwości (Hz)

    lastmillis = millis();          // Aktualizacja lastmillis

    noInterrupts();                   // W trakcie kalkulacji wyłącz obsługę przerwań
    rpm = half_revolutions * 30;
    half_revolutions = 0;           // Restart licznika obrotów
    interrupts() ; //Przywróć przerwania

    mph = diameter / 12 * 3.14 * rpm * 60 / 5280;//Odczyt prędkości wiatru w milach/godzinę
    mph = mph * 3.5; // Kalibracja błędu odczytu, wartość należy dobrać we własnym zakresie
    kmh = mph * 1.609;// Zamiana mil/godzinę na km/h
    winddir = get_wind_direction(); //Odczyt kierunku wiatru

    Serial.print("\t KMH=\t"); //Przesłanie odczytanych danych do portu szeregowego
    Serial.println(kmh);
    Serial.println();
    Serial.print("winddir=\t");
    Serial.println(winddir);

  }
}

Odczyt kierunku wiatru

W przypadku odczytu kierunku wiatru została stworzona funkcja, która sprawdza czy odczytana wartość mieści się w założonym wcześniej zakresie. Jeśli tak to zwraca wynik w postaci kierunku wiatru. Tak jak pisaliśmy wcześniej na nasze potrzeby użyliśmy 8 podstawowych kierunków. Jesli ktoś chce rozszerzyć odczyt do 18 kierunków należy dopisać kolejne linijki z odpowiednimi wartościami ADC. Najłatwiej jest skorzystać z przykładowego programu odczytującego wartość ADC i spisać sobie gdzieś na kartce wartości, a następnie dopisać do powyższego kodu (kod dostępny będzie poniżej).

 if ((adc >760) && (adc < 799)) return (0); //Jeżeli wartośc będzie się zawierała w podanym zakresie, zostanie zwrócony kierunek wiatru
  if ((adc >450) && (adc < 470)) return (45);
  if ((adc > 85) && (adc < 100)) return (90);
  if ((adc >170) && (adc < 199)) return (135);
  if ((adc >270) && (adc < 299)) return (180);
  if ((adc >610) && (adc < 640)) return (225);
  if ((adc >930) && (adc < 960)) return (270);
  if ((adc >870) && (adc < 899)) return (315);

Odczyt danych w oknie portu szeregowego

Kod do odczytu wartości ADC

const int analogInPin = A0;  // Pin A0 do którego podłączony jest czujnik kierunku wiatru


int sensorValue = 0;        // wartość odczytana z portu ADC


void setup() {
  Serial.begin(9600);
}

void loop() {
  sensorValue = analogRead(analogInPin); //odczyt z czujnika kierunku wiatru
  Serial.print("sensor = " );
  Serial.print(sensorValue); //odczytana wartość ADC przesłana do portu szeregowego (zakres 0-1024)
  delay(2);
}

Pomiar prędkości wiatru

Jest jednak jedna rzecz, o której warto wspomnieć. W przypadku funkcji odczytującej prędkość wiatru zostało użyte przerwanie. Czym jest przerwanie?

Obsługa przerwania polega na odłożeniu aktualnie wykonywanego kodu, wykonania zdefiniowanych dla danego przerwania akcji i powrocie do wykonywania programu właściwego. Co się z tym wiąże? Przede wszystkim program wie, że jeśli stan pinu zmieni się z wysokiego na niski (czyli zostanie zwarty kontaktron), to należy udać się do funkcji liczącej prędkość wiatru i następnie zwrócić wynik w postaci prędkości wiejącego wiatru. Po zakończeniu tego można wrócić do obsługi reszty programu.

Więcej o przerwaniach możecie poczytać w artykule na Akademii poświęcony temu tematowi.

Jeśli chwilę przetestujemy nasz kod, okaże się, że dla wolnych obrotów (czyli słabego wiatru) pomiar jest bardzo nieprecyzyjny. Wynika to z tego, że jak mamy jeden impuls na sekundę, to dopóki nie będzie dwóch impulsów na sekundę obliczenie prędkości się nie zmieni, choć różnica w prędkości wiatru może być spora.

Dlatego zaproponujemy drugi sposób pomiaru prędkości. Zamiast liczyć ilość zwarć kontaktronu w sekundzie będziemy mierzyć czas pomiędzy zwarciami. Różnica pojawi się w funkcji obsługującej przerwanie:

void rpm_fan() {
  unsigned long static last_event = 0;
  if (millis() - last_event < 5) {   //debouncing
    return;
  }
  half_revolution_time = (millis() - last_event);
  last_event = millis();
}

Jak widać, teraz mierzymy czas półobrotu a nie liczbę impulsów. Obliczenie ile to jest obrotów na minutę - wystarczy podzielić 30 s (wyrażone w milisekundach) przez czas półobrotu:

    rpm = (30000 / half_revolution_time) ;

Jaka jest różnica w stosunku do zliczania zwarć kontaktronu? Jak już pisaliśmy, przy słabym wietrze (mała liczba zwarć na sekundę) pomiar będzie o wiele dokładniejszy. Również nie ma potrzeby zapisu zawartości zmiennych używanych przez obsługę przerwania z poziomu głównego programu, co zapobiega trudnym do wykrycia błędom (nie ma potrzeby zerowania liczby zwarć).

Na minus tego rozwiązania przemawia sytuacja gdy wiatr przestaje wiać. Ponieważ nie ma zwarć kontaktronu, nie ma wywołań funkcji rpm_fan. Czas półobrotu nie jest aktualizowany i ciągle jest zwracana ostatnia wartość, czyli Arduino zwraca ciągle prędkość wiatru większą od zera. Jak można to rozwiązać? Ponieważ half_revolution_time jest wyrażony w milisekundach, jest niewielka szansa, że np przez 3 kolejne sekundy, czas ten będzie identyczny. Kod mógłby pamiętać poprzedni czas półobrotu i który raz widzi go bez zmiany. Jeśli np widzi 3 raz pod rząd dokładnie taki sam czas (a jednocześnie jest on większy od powiedzmy 100 ms) to wówczas arbitralnie przyjmuje wartość rpm równą zero.

Wnioski

Korzystając z powyższego programu obsługującego czujniki prędkości i kierunku wiatru już w tym momencie możemy zbudować bardzo prostą stację pogodową. Dodając do tych czujników możliwość odczytu temperatury, wilgotności lub ciśnienia powietrza możemy stworzyć naprawdę zaawansowaną stację pogodową, która będzie dużo dokładniejsza od tych sprzedawanych w supermarketach, nie mówiąc już o możliwościach jakie nam to daje. Możemy np. przesyłać odczytane wartości do jednego z serwisów internetowych (np.thingspeak.com), który na ich podstawie będzie nam rysował wykresy, pozwalając nam zobaczyć jaki jest np. trend, albo jak zmienia się temperatura w ciągu dnia. Taką stacje można również podłączyć pod system automatyki domowej, dzięki czemu w zależności od warunków pogodowych możemy albo włączać, albo wyłączać podlewanie w ogródku, czy przysyłać infromacje do właściciela, iż w obecnej chwili pada deszcz lub temperatura jest wysoka (wtedy można np. zdalnie załączyć klimatyzacje w domu, aby po przyjściu z pracy mieć chłód). Możliwości jest wiele, a jedynym ograniczeniem jest wyobraźnia użytkowników.

Pliki do pobrania