Jak stworzyłem w pełni funkcjonalną stację pogodową Arduino?
Opublikowany: 2022-03-11AKTUALIZACJA: Prace nad naszą stacją pogodową Arduino były kontynuowane po opublikowaniu tego artykułu, których kulminacją było wydanie Open Weather Station (OWS). Sprawdź go, aby uzyskać dodatkowe aktualizacje, zasoby, kod i nowe samouczki.
O co w tym wszystkim chodzi?
Kitesurfing to jeden z najbardziej uzależniających sportów na świecie. Wystarczy deska do kitesurfingu, zbiornik wodny i kilka akcesoriów. To świetny sposób na kontakt z naturą, uwolnienie umysłu i ćwiczenia. Poza tym możesz naprawdę zwariować.
Więc w czym problem?
Och, zapomniałem o jednym podstawowym warunku: wiatru. I tu właśnie mamy problem: nigdy nie wiesz, czy będzie wiatr, chyba że mieszkasz tuż przy swoim ulubionym miejscu do kitesurfingu.
Mieszkam w Kordobie w Argentynie, około 130 kilometrów (~80 mil) od jeziora, w którym uprawiam kitesurfing. To mniej więcej dwie godziny jazdy, z którą mogę sobie poradzić. Ale nie mogę sobie poradzić z tym, że prognozy pogody są niedokładne. A tam, gdzie mieszkam, dobre warunki wietrzne trwają tylko kilka godzin. Ostatnią rzeczą, którą chcesz zrobić, to uporządkować swój poniedziałkowy harmonogram na kitesurfing i po dwóch godzinach jazdy przeklinać bogów na bezwietrznym jeziorze.
Musiałem poznać warunki wietrzne mojego ulubionego miejsca do kitesurfingu – w czasie rzeczywistym. Postanowiłem więc zbudować własną stację pogodową.
Mierzenie pogody w czasie rzeczywistym — w nieprzyjaznym środowisku
Celem było dostarczanie danych o pogodzie w czasie rzeczywistym do przeglądarki w domu:
Zanim przejdę do konkretów, poświęćmy chwilę na rozważenie kluczowych pytań i zastrzeżeń związanych z takim projektem:
- Jak stworzyć stację pogodową, która nie będzie ani cenna, ani atrakcyjna dla złodzieja?
- Jak mogę ograniczyć do minimum koszty sprzętu i czas rozwoju?
- Jak mogę mierzyć i uzyskiwać dostęp do danych pogodowych w czasie rzeczywistym i wyświetlać je w użyteczny sposób?
- Wymagane pomiary: wiatr i porywy wiatru, kierunek wiatru, deszcz, ciśnienie atmosferyczne, temperatura, wilgotność
- Połącz stację z Internetem
- Przechowuj i pobieraj lokalne dane pogodowe
- Komunikacja między stacją pogodową a serwerem
- Jak mogę zredukować konserwację do (prawie) zera?
- Zarządzaj zawieszaniem oprogramowania
- Zarządzaj utratą łączności
- Zarządzaj utratą dostaw energii
Przywitaj się z moim małym przyjacielem!
Możesz pomyśleć, że rękawica jest po to, aby stacja wydawała się bardziej przyjazna; ale w rzeczywistości służy do testowania czujnika barometrycznego (ciśnienie rękawicy wzrasta wewnątrz napompowanej rękawicy). Po prawej stronie możesz zobaczyć stację w jej ostatecznej lokalizacji, na szczycie pobliskiej wieży.
Zaprojektowałem i zaprogramowałem również stronę internetową o kitesurfingu, która zawiera wykres pomiarów stacji w czasie rzeczywistym, aby pomóc społeczności kitesurfingu. W końcu stworzyłem grupę kitesurfingową na Facebooku.
To cudownie! Więc jak to zrobiłeś?
Cóż, omówię kolejno każdy punkt:
„Jak mogę stworzyć stację pogodową, która nie będzie ani cenna, ani atrakcyjna dla złodzieja?”
Był to krytyczny czynnik i pod wieloma względami napędzał resztę procesu projektowania. Większość gotowych stacji poniżej linii 2000 USD wymagała połączenia USB z komputerem. Gdyby złodziej rozpoznał, że stacja ma obok siebie komputer, byłby to koniec rzeczy, ponieważ koszt wymiany komputera i stacji przekroczyłby mój osobisty budżet. Dlatego zdecydowałem się przetestować kilka platform sprzętowych, aby wdrożyć stację od podstaw, przy niższych kosztach.
„Jak mogę ograniczyć koszty sprzętu i czas rozwoju do minimum?”
Sam pokrywałem koszty tego pobocznego projektu i wykonywałem całą pracę w wolnym czasie, więc oczywiście był to duży problem. Zacząłem od popularnego PIC32 i kilku wstępnie zmontowanych modułów Ethernet z mikrochipami, ale koszty nie były tak niskie, jak się spodziewałem, a montaż i rozbudowa sprzętu wymagały zbyt dużego narzutu. Następnie zacząłem szukać Arduino: sprzętu i oprogramowania typu open source do prototypowania elektroniki przy użyciu języka C. To było dokładnie to, czego chciałem i mogłem kupić moduły na DealeXtreme. Udało mi się zacząć zabawę z zaledwie 15 dolarami wydatków i dwoma dniami mojego czasu.
Oczywiście Arduino ma też swoje ograniczenia: tylko 2 KB pamięci RAM i 32 KB dla mojego skompilowanego oprogramowania — to nie pozostawia wiele miejsca na wymyślne ciągi znaków lub bezużyteczne zmienne 1 .
„Jak mogę mierzyć i uzyskiwać dostęp do danych pogodowych w czasie rzeczywistym i wyświetlać je w użyteczny sposób?”
Obecnie moja stacja może mierzyć: prędkość wiatru, poryw wiatru, kierunek wiatru, temperaturę, wilgotność, deszcz i ciśnienie atmosferyczne. Temperatura, wilgotność i ciśnienie są obsługiwane przez kilka bibliotek, co znacznie ułatwiło życie.
Pomiary prędkości wiatru i deszczu były trochę chaotyczne. Czujniki działają poprzez otwieranie i zamykanie łącznika (kontaktronu). Dlatego musiałem zaimplementować przerwania sprzętowe, aby złapać czujnik, gdy tylko wyzwoli wejście. Oznacza to, że musiałem wywołać jakąś metodę:
attachInterrupt(RAINGAUGE_PIN, countRainCycles, FALLING);
Przerwanie to przerwałoby normalne wykonanie kodu i wywołało funkcję countAnemometerCycles lub countRainCycles, gdy tylko przełącznik doświadczy zbocza opadającego, wywołanego zamknięciem lub otwarciem obwodu. Kilka zmiennych jest zwiększanych przy każdym wyzwalaniu przełącznika. (Później ważysz te zmienne, aby uwzględnić konwersje jednostek).
void countRainCycles() { rainCyclesCounter++; // This is easy! And it actually works. }
Ale nie tak szybko! Proces ten generuje setki fałszywych wyzwalaczy w wyniku efektu odbijania się przełącznika, który jest charakterystyczny dla każdego przełącznika sprzętowego. Na szczęście istnieją rozwiązania tego problemu zarówno sprzętowe, jak i programowe.
O efekcie odbijania
Efekt odbijania występuje w wyniku fizycznego otwarcia lub zamknięcia styków przełącznika, które nawiązują kontakt z resztą obwodu. Kiedy styki zaczynają się rozdzielać (otwierać przełącznik) lub łączyć (zamykać przełącznik), mogą powstawać niewielkie łuki elektryczne, a także mechaniczna elastyczność w obwodzie, która powoduje włączanie i wyłączanie obwodu na kilka milisekund. Po przekręceniu włącznika światła ten efekt nie jest widoczny; ale kiedy dołączysz przerwanie do opadającej krawędzi sygnału, ten efekt odbijania wyzwala mnóstwo przerwań. Więcej tutaj.
Zaimplementowałem zarówno sprzętowy obwód debounce, jak i podobną wersję programową. Ale jak dokładnie zaimplementować debounce oprogramowania? Łatwo! Po wystąpieniu pierwszego oczekiwanego wyzwalacza „odczekaj” wystarczająco dużo czasu, aż odbicie się ustabilizuje, zanim zaczniesz nasłuchiwać nowych przerwań. Można to osiągnąć w kilku linijkach C:
void countRainCycles() { if (nextTimeRainIterrupt == 0 || nextTimeRainIterrupt < millis()) { rainCyclesCounter++; // The interrupts counter nextTimeRainIterrupt = millis() + 100; // Wait 100msecs before next trigger } }
Funkcja millis() zwraca aktualny czas wykonania w milisekundach od momentu włączenia Arduino. Warto również zauważyć, że zmienne te muszą być zdefiniowane jako niestabilne, aby poinstruować kompilator, aby nie optymalizował wykonania i dlatego unikał niedokładnych wartości podczas przerwań sprzętowych.

Jakoś potrzebowałem stacji do przechowywania zgromadzonych danych i okresowego wysyłania tych pomiarów do bazy danych MySQL. Dodałem więc moduł Ethernet z gniazdem SD, aby rejestrować wartości i pobierać je za każdym razem, gdy użytkownik (serwer) łączy się ze stacją. Podczas testów w domu z łącznością ADSL działało to zadziwiająco dobrze, ale prawie straciłem włosy, kiedy testowałem to „w terenie” z Internetem 3G (przy użyciu modemu 3G), ponieważ stacja losowo resetowała się, gdy próbowałem odzyskać pomiary! Po gruntownych testach w końcu odkryłem, że przykłady podane w całym Internecie, które opisują „serwowanie” danych podłączonemu klientowi, nie uwzględniały tego, że połączenie może być tak słabe, że połączenie z klientem może zostać utracone w połowie transmisji pakietowej, powodując bufor wyjściowy by się przepełnił. Ale dlaczego zerwane połączenie miałoby powodować przepełnienie bufora? Powiedzmy, że rozpoczyna się sesja transmisji i stacja zaczyna zapełniać bufor wyjściowy danymi. W idealnym przypadku klient zużywa ten bufor szybciej, niż zostaje wypełniony. Jednak w przypadku połączenia z modemem 3G tak się nie stało! Połączenie z klientem było zdecydowanie za słabe, więc bufor zapełniał się szybciej niż był zużywany, co spowodowało zarówno przepełnienie bufora, jak i nagłe ponowne uruchomienie stacji.
Aby rozwiązać ten problem, musiałem dodać funkcję do biblioteki Ethernet dostarczonej z Arduino, która wyglądała mniej więcej tak:
int EthernetClient::free() { if (_sock != MAX_SOCK_NUM) return W5100.getTXFreeSize(_sock); return 0; }
Następnie mogłem sprawdzić, czy klient ma trochę miejsca w buforze, zanim spróbuję wypełnić go większą ilością danych:
while (file.available() > 0) { if (client.free() > 0) { // This was key to solving the issue c = file.read(); client.print((char)c); } else { // No free buffer? Ok, I'll wait a couple of millis... delay(50); } } file.close();
Przy okazji, jeśli jesteś zainteresowany programowaniem Arduino, oto świetny przewodnik.
Kolejnym ciekawym zadaniem było wykonanie logu LIFO. Dlaczego było to konieczne? Cóż, zazwyczaj, gdy zapisuję pomiary do danego pliku, podejście jest proste: otwórz plik, dołącz nowe próbki na końcu i zamknij plik. Ale powiedzmy, że chcę pobrać ostatnie 1000 pomiarów, posortowane chronologicznie. Te pomiary znajdują się na końcu pliku; więc powinienem otworzyć plik, przesunąć kursor do końca, wyprowadzić ostatnie pomiary, a następnie przywrócić kursor pliku do poprzedniego pomiaru i wyprowadzić go, szukając ogranicznika próbki, aby wykryć, gdzie rozpocząć i zatrzymać. Arduino nie ma wystarczającej ilości pamięci RAM ani mocy procesora, aby szybko wykonać ten proces, więc potrzebowałem innego podejścia. Zamiast tego zdecydowałem się wysłać plik w odwrotnej kolejności do serwera, a następnie przywrócić literały ciągów z powrotem po stronie serwera:
unsigned long filePosition = file.size(); file.seek(filePosition); while (filePosition >= 0) { if (client.free() > 0){ file.seek(filePosition); c = file.peek(); if (c != -1) { client.print((char)c); } if (filePosition <= 0) { break; } filePosition--; } }
Z każdym doświadczeniem jako programista PHP, łatwo jest uzyskać najnowsze próbki z postaciami we właściwej kolejności:
// $output has the reversed string measures, each sample is delimited by ; $rows = split(";", trim($output)); array_walk_recursive($rows, 'reverseString'); if (strlen($rows[0]) == 0) { array_shift($rows); // Remove the first line if empty } function reverseString(&$row, $key) { $row = trim(strrev($row)); } // $rows is now the array of the latest samples :)
Po stronie serwera konfiguruję proces cron, aby co dwie minuty pobierał najnowsze pomiary i wstawiał dane do silnika MySQL. Aby wyświetlić dane, stworzyłem stronę www.kitesurfcordoba.com.ar i użyłem jQuery do automatycznej aktualizacji wykresów (które same są generowane przy użyciu pChart v2.0, świetnej biblioteki open-source).
Było wiele innych sztuczek niezbędnych do działania, związanych zarówno z inżynierią oprogramowania, jak i sprzętu, ale ciągnąłem wystarczająco długo - więc porozmawiajmy o minimalizacji konserwacji.
„Jak mogę zredukować konserwację do (prawie) zera?”
Był to poważny problem, ponieważ z pewnością nie jest mi łatwo dotrzeć na stację – gdybym miał jechać dwie godziny drogi tylko po to, by naprawić drobną usterkę, to nie musiałbym jej w ogóle wpuszczać (ja nie wspomniałem o tym wcześniej, ale po tym wszystkim, przez co przeszliśmy, stacja to właściwie „ona” i ma na imię Dorothy).
Więc o jakich rodzajach błędów mówimy tutaj? Na przykład: oprogramowanie może się zawiesić, sieć może stracić łączność, zasilanie może ulec awarii (i tak się dzieje) itp.
Zasadniczo stacja musi wykonać jak najwięcej samonaprawy. Dlatego zastosowałem zarówno miękkie, jak i twarde psy stróżujące. Dla tych, którzy nie są zaznajomieni, watchdog to oprogramowanie lub sprzęt, które sprawdza, czy system działa poprawnie, a jeśli nie, próbuje przywrócić go do życia. Arduino ma wbudowany watchdog, którego możesz użyć. Ustawiłem czas oczekiwania na 8 sekund: jeśli połączenie trwa dłużej niż ten limit czasu, programowy watchdog zresetuje kartę.
wdt_enable(WDTO_8S); // "wdt" stands for "watchdog timer"
Uwielbiam tę funkcję. Jednak zdarzają się sytuacje, w których płyta się resetuje, a moduł Ethernet nie. Czemu? Cóż, jest to stosunkowo przystępna cenowo płyta prototypowa, a nie bardzo drogie, odporne na awarie urządzenie (z pewnością nie powinieneś budować z nim rozrusznika serca). Aby przezwyciężyć tę wadę, musiałem zhakować Arduino, łącząc wejście resetowania sprzętowego z wyjściem cyfrowym na samej płycie. Aby uniknąć pętli resetowania, należy dodać również kilka wierszy kodu:
void setup() { digitalWrite(RESET_ARDUINO_PIN, HIGH); // Set it to HIGH immediately on boot pinMode(RESET_ARDUINO_PIN, OUTPUT); // We declare it an output ONLY AFTER it's HIGH digitalWrite(RESET_ARDUINO_PIN, HIGH); // Default to HIGH, set to LOW to HARD RESET ...
Następnie mogłem wykonać reset sprzętowy Arduino i wszystkich znajdujących się na nim modułów (w tym modułu Ethernet), po prostu wywołując digitalWrite(RESET_ARDUINO_PIN, LOW)
, co przywróciło Dorothy do życia po kilku sekundach.
Dodatkowo płyta automatycznie restartuje się po utracie energii. A jeśli połączenie z Internetem zawiedzie, wykorzystujemy możliwości przechowywania karty SD (dane mogą być przechowywane na karcie przez ponad tydzień, a serwer może pobrać stare dane, aby odzyskać brakujące próbki). Połączenie wszystkich tych funkcji daje nam bardzo solidną stację pogodową, która może przetrwać nieprzyjazne warunki, do monitorowania której została zbudowana. W sumie kosztowało mnie to około 300 dolarów.
A na koniec
Stacja działa od grudnia 2012 roku. Do tej pory nie zawiodła (a jeśli tak, to stacja na tyle szybko wracała do zdrowia, że społeczność kitesurfingowa i ja nie zauważyliśmy). Jest około 500 kitesurferów, którzy regularnie sprawdzają stację pogodową przed podróżą na spot. Tak więc poza nagrodą za rozwiązanie niektórych trudnych wyzwań technicznych, miałem również okazję zapewnić grupie ludzi przyjemniejsze wrażenia z kitesurfingu.
1 Początkowo używałem Arduino Uno. Później przeszedłem na Arduino Mega ze względu na potrzebę zwiększenia pamięci RAM i pamięci flash.
Powiązane: Praca z próbkowaniem dźwięku ESP32