Zautomatyzowane Systemy Wytwarzania Stacja Meteorologiczna AiR V sem. Gr. A4/2 Bartosz Orszulak Wiktor Pilewski Bartłomiej Wicher Adam Szmajdziński 26 stycznia 2011 1
Spis treści 1 Założenia projektu 4 1.1 Głowne założenia projektu........................... 4 2 Opis modułów 5 2.1 Płyta głowna.................................. 5 2.2 Zasilanie..................................... 6 2.3 Komunikacja.................................. 9 2.4 Obudowy.................................... 10 2.4.1 BigBox.................................. 10 2.4.2 OrangeBox............................... 10 2.4.3 TubaBox................................. 11 2.4.4 WindBox................................ 11 3 Opis czujników i urządzeń pomiarowych 11 3.1 SHT71 - pomiar temperatury i wilgotności.................. 11 3.2 HP01S - pomiar ciśnienia............................ 13 3.3 DS18B20 - pomiar temperatury........................ 14 3.4 TCRT5000 - transoptor odbiciowy....................... 14 3.5 Wiatromierz................................... 15 3.6 Deszczomierz.................................. 17 4 Program 21 4.1 Obsługa czujnika SHT71............................ 21 4.2 Określanie kierunku wiatru........................... 25 4.3 Obsługa PCF8583................................ 25 4.4 Komunikacja UART.............................. 27 4.4.1 Obsługa ze strony mikrokonrolera................... 27 4.4.2 Program odbierający dane....................... 29 4.5 Obsługa deszczomierza............................. 32 4.6 Makroskopowy algorytm działania programu................. 34 5 Testy 37 5.1 Wstępny test SHT71.............................. 37 5.2 Interface użytkownika poprzez stronę www.................. 38 5.2.1 Plik pomiarów.............................. 39 5.2.2 Pola wejściowe............................. 39 5.2.3 Wykresy................................. 39 5.3 Całodniowy test systemu............................ 40 2
Streszczenie... coraz to częściej w wielu firmach i zakładach produkcyjnych zwraca się uwagę na warunki atmosferyczne, a tym samy powstaje konieczność poznania dokładnej prognozy pogody krótkoterminowej i wiarygodnej długoterminowej. Niestety, prognozowanie pogody jest zagadaniem bardzo skąplikowanym i trudnym, dlatego masowe prognozy na bazie zdjęć satelitarnych dla danego regionu terenu często się nie zgadzają z rzeczywistością. Najlepsze prognozy pogody otrzymuje się przez ciągłe badanie i obserwowanie otoczenia, ale są to prognozy krótkoterminowe(około od 1 do 12 godzin). Możliwe że tak krótki czas nie jest przydatny, ale w rzeczywistości może bardzo dużo wnieść ważnych informacji w przypadkach zakładów lub firm produkcyjnych czy transportowych zmuszonych do działania w warunkach zewnętrznych(poza halami lub w innych miejscach wrażliwych na warunki zewnętrzne). Projekt Stacji METEO I umożliwia zbieranie najważniejszych danych pomiarowych do dalszego wykorzystania. Dzięki swojej budowie modułowej i uniwersalności zamontowania dodatkowych czujników jest ciekawą pozycją badawczo-naukową i użytkową, którą można byłoby śmiało stosować wielu dziedzinach. Bartosz Orszulak, pomysłodawca 3
1 Założenia projektu 1.1 Głowne założenia projektu W związku z budową stacji meteorologicznej, zaplanowano realizację następujących celów: 1. Poszerzenie wiedzy na temat czujników i przewidywaniu pogody, 2. zbudowanie na bazie modułów, jednostki badawczo-naukowej do zbierania i zapisywania danych pogodowych, 3. budowa modułowa stacji, 4. umożliwienie komunikacji i sterowania stacją drogą radiową, 5. pogłębienie wiedzy elektrotechnicznej, 6. zbudowanie systemu awaryjnego zasilania, 7. nauka projektowania układów elektronicznych, 8. przetestowanie odporności układu na warunki zewnętrzne oraz na długotrwale działanie w niesprzyjających warunkach. Projekt można podzielić na dwie części a mianowicie: ˆ stację meteorologiczną wykonującą pomiary, ˆ stację roboczą zbierającą, przetwarzającą i wizualizującą pomiary, a także umożliwiającą zdalne sterowanie, wyłączanie i diagnostykę czujników. Stąd też schemat blokowy zamieszczony na rys. 1 dzieli się na dwa zasadnicze elementy. Górna część schematu przedstawia stację roboczą, dolna zaś właściwą stację meteorologiczną jako w pełni autonomiczne urządzenie. 4
2 Opis modułów 2.1 Płyta głowna Rysunek 1: Schemat blokowy stacji meteorologicznej Wszystkie peryferia podłączone są do płyty głównej poprzez złącza zaciskowe. Pozwala to na prosty montaż/demontaż stacji bez używania lutownicy oraz na wymianę czujników używając tylko śrubokręta. Na płycie głownej modułu stacji można znaleźć następujące urządzenia: 1. mikrokontroler ATmega32L 2. zegar czasu rzeczywistego PCF8583 (wraz z zapasową baterią) 3. moduł komunikacji RS232 = USB FT232RL 4. gniazdo karty pamięci SD 5. złącze do podłączenia modułu ZigBee 6. złącze programatora 7. złącza do podłączenia czujników, 8. diagnostyczny czujnik temperatury DS18B20, 9. tranzystory odcinające zasilanie czujników. Mikrokontroler ATmega jest sercem całej stacji. Został wybrany spośród szerokiej gamy mikrokontrolerów AVR, ponieważ spełnia wszystkie stawiane wymagania, tj.: ˆ ˆ posiada wystarczającą liczbę wejść/wyjść do podłączenia wszystkich czujników, sprzętowo obsługuje transmisję szeregową, 5
ˆ posiada przetwornik analogowo-cyfrowy, ˆ może pomieścić program o wielkości do 32kB, ˆ jest to wersja L, a więc o niskim zużyciu prądu (co będzie ważne przy pracy na zasilaniu awaryjnym). Zegar PCF8583 posłuży do odliczania czasu niezależnie od programu, a także dzięki awaryjnemu zasilaniu bateryjnemu przy wyłączonej stacji (bateria 3V umieszczona na płycie głównej). Dzięki temu możliwe będzie uniknięcie ustawiania godziny przy każdym uruchomieniu. Czas i data uzyskana dzięki temu zegarowi posłuży do oznaczania pomiarów i w takiej formie zostaną one zapisane na karcie SD. Komunikacja ATmega = PCF8583 odbywa się za pośrednictwem inferface u I2C. Na karcie pamięci SD przechowywane będą pomiary do czasu ich przesłania na komputer. Konieczność jej zastosowania wynika z tego, że 2kB pamięci RAM mikrokontrolera są niewystarczające. Diagnostyczny czujnik temperatury DS18B20 ma na celu zapobieganie uszkodzeniu elementów płyty głównej wynikających z możliwości wystąpienia ekstremalnych warunków wewnątrz obudowy. W razie wystąpienia takiej sytuacji, mikrokontroler wyłączy najbardziej zagrożone moduły. Tranzystory odcinające zasilanie zostały zastosowane do użycia w dwóch przypadkach: praca na zasilaniu awaryjnym (czujniki o największym poborze mocy zostaną wyłączone) oraz wystąpienie warunków niesprzyjających sprawnemu działaniu czujników. Ze względu na to, że ATmega nie posiada sprzętowej obsługi USB tylko transmisję szeregową, a nowe komputery natomiast nie posiadają już portu RS232, układ FT232RL pośredniczy w transmisji Stacją METEO = PC. Wykorzystywany będzie w sytuacjach awaryjnych lub w celach diagnostycznych, ponieważ głównym torem transmisji danych ma być komunikacja przez ZigBee. Całość może być zasilana napięciem od 3 do 5V. 2.2 Zasilanie Moduł zasilania został stworzony do pracy ciągłej z sieci 230V AC oraz w przypadku odłączenia zasilania z sieci do pracy tymczasowej z akumulatorów Ni-Cd. Dzięki zastosowaniu układu MAX713 uzyskaliśmy automatyczną ładowarkę do akumulatorków, która też kontroluje przełączanie na zasilanie awaryjne, które do celowo ma wytrzymać ponad 3 godziny w temperaturze od -10 C do 65 C. Dodatkowo dzięki zastosowaniu przetwornic napięciowych na zasilanie żądanym napięciem większości układów, udało nam się zredukować pobór mocy do niewielkich wartości. Schemat głównego układu zasilającego opartego na MAX713 (schemat zalecany przez producenta) przedstawiono na rys. 3. Wzmacniacz pomiarowy wymaga zasilania napięciem min +11V, co jest warością przewyższającą znacznie wartość napięcia zasilania pozostałych modułów. Aby nie dopuścić do zbędnego tracenia energii w ciepło w stabilizatorach szeregowych zdecydowano się podwyższyć napięcie zasilania dla wzmacniacza pomiarowego za pomocą przetwornicy impulsowej. Przetwornica taka charakteryzuje się sprawnością znacznie wyższą niż stabilizator szeregowy, obniżający napięcie o 50%. Schemat aplikacyjny przetwornicy przedstawiono na rys. 4 6
Rysunek 2: Schemat ideowy płyty głównej 7
Rysunek 3: Schemat ideowy układu zasilającego stację i pełniącego jednocześnie rolę ładowarki akumulatorów rezerwowych Rysunek 4: Schemat ideowy przetwornicy zasilającej wzmacniacz pomiarowy schemat elektryczny ze strony http://www.eleccircuit.com 8
Rysunek 5: Schemat ideowy podłączenia ZigBee(schemat zalecany od producenta z J- TAGiem) 2.3 Komunikacja Stacja METEO I od samego początku miała być zdalnie sterowana, dzięki czemu zyskała ona na mobilności i wszechstronności umieszczania jej w dowolnym miejscu, gdzie znajduje się wyłącznie kabel zasilania. Poza tym, miała ona współpracować z dowolnym sprzętem komputerowym oferującym port USB. Dzięki zastosowaniu modułów radiowych ZigBee uzyskaliśmy pożądany efekt oraz możliwość podłączenia się bezpośrednio do stacji. W przypadku awarii modułu radiowego, istnieje możliwość podłączenie się kablem USB B(typ drukarkowego) do stacji bez konieczności rozhermetyzowania jej, poprzez zabezpieczone gniazdo USB umieszczone w gnieździe na obudowie poprzez układ FT232RL. Rysunek 6: Schemat ideowy konwertera sygnału USB = RS232 z linią zasilającą 9
Rysunek 7: Obudowa BigBox z układem zasilającym 2.4 Obudowy Stacja meteo docelowo ma się składać z czterech modułów: 1. BigBox - moduł główny zawierający układ zasilania, główną płytkę z mikrokontrolerem Atmega 32, łączący wszystkie pozostałe moduły, 2. OrangeBox- moduł ochronny na czujniki: termometr, wilgociomierz i barometr, 3. TubaBox- moduł deszczomierza, 4. WindBox - moduł wiatromierza - siły oraz kierunku. Przedstawiony zostanie opis mechaniczny poszczególnych modułów. 2.4.1 BigBox Największa i najlepiej zabezpieczona obudowa na elektronikę (rys. 7). Obudowa została wykonana w standardzie IP-65, czyli jest w pełni hermetyczna. Podczas naszego projektu zastosowaliśmy mufy do wyprowadzenia kabli o IP-55 (nie hermetyczne w pełni, ale wystarczające w przypadku naszego projektu) zamontowane pionowo w dół. Dzięki dużym wymiarom oraz wewnętrznym szynom montażowym jest to idealna obudowa do testowania nowych układów i ciągłych modyfikacji. Obudowa posiada specjalnie zabezpieczone gniazdo serwisowe USB-B do bezpośredniego połączenia stacji meteo z komputerem, dzięki któremu w przypadku uszkodzenia modułu radiowego osoba chcąca sprawdzić czy są nowe dane do pobrania może tego dokonać bez konieczności rozkręcania obudowy. Dzięki temu zmniejsza się ryzyko uszkodzenia stacji. Każda nie wykorzystywana mufa jest zabezpieczona gumowym korkiem w celu uniknięcia zalania. 2.4.2 OrangeBox Jest to obudowa chroniąca czujniki temperatury(rys. 8), wilgotności i barometr przed bezpośrednim działaniem deszczu, śniegu oraz słońca. Jej specyficzna budowa zapewnia 10
Rysunek 8: Obudowa OrangeBox bardzo dobry przewiew dzięki czemu otrzymujemy rzeczywiste dane warunków zewnętrznych. Jest to lekka obudowa plastikowa połączona ocynkowanymi śrubami. 2.4.3 TubaBox Obudowa mechanizmu zrobiona jest z wytrzymałej rury kanalizacyjnej(110mm).(rys. 9) Lejek umieszczony wewnątrz jest specjalnie zwężony, aby strumień wody idealnie padał na urządzenie pomiarowe. Elektronika ukryta wewnątrz jest chroniona jest przed oddziaływaniem deszczu, śniegu, i wiatru. Przestrzeń pod lejkiem i elektronika docelowo będzie zatopiona w ciepłym kleju, aby była całkowicie wodoodporna. Deszczomierz posiada prostą gumową przysłonę na obudowie do szybkiego sprawdzenia stanu mechanicznego czujnika. 2.4.4 WindBox Niestety moduł wiatromierza jeszcze nie powstał z przyczyn mechanicznych. Nie rozwiązany został jeszcze problem łożyskowania chorągiewek. Nie udaęo nam się jeszcze uzyskać niskich oporów i wysokiej trwałości. 3 3.1 Opis czujników i urządzeń pomiarowych SHT71 - pomiar temperatury i wilgotności SHT71 jest precyzyjnym 14-bitowym czujnikiem temperatury i 12 bitowym czujnikiem wilgotności. Podstawowe parametry układu przedstawiono w tabeli 1. Wilgotność względna RH przedstawiona jest w nieliniowej formie, linearyzacji można dokonać wykorzystując zależność 3 dla temperatury oraz zależność 2 dla wilgotności względnej: 11
Rysunek 9: Obudowa TubaBox Tabela 1: Podstawowe parametry układu SHT71 Temperatura pracy -40... 125 C Tolerancja + 0.5% Zakres pomiarowy -40... 123.8 C Zakres napięć zasilajacych DC 2.4... 5.5V Wyprowadzenie danych Cyfrowe (2 przewodowe) 12
Rysunek 10: Schemat ideowy podłączenia czujnika SHT71 (źródło [2] Tabela 2: Podstawowe parametry układu HP01S Temperatura pracy -40... 85 C Tolerancja +55hpa Zakres pomiarowy 750... 1100 hpa Zakres napięć zasilajacych DC 2.2... 3.6V Wyprowadzenie danych Cyfrowe (I 2 C) 16-bitowe RH linear = c 1 + c 2 SO RH + c 3 SO 2 RH (1) Odczyt temperatury przeprowadza się analogicznie: T = d 1 + d 2 SO T (2) Po obliczeniu temperatury oraz wilgotności względej, dokonuje się temperaturowej korekcji wilgotności: RH true = (T C 25) (t 1 + t 2 SO RH ) + RH linear (3) Gdzie: SO jest liczbą naturalną wysyłaną cyfrowo do mikrokontrolera. Stałe c 1, c 2, c 3, d 1, d 2, t 1, t 2 podane są w karcie katalogowej elementu. 3.2 HP01S - pomiar ciśnienia HP01S jest 16 bitowym czujnikiem ciśnienia. Podstawowe parametry zestawione są w tabeli 2. Schemat ideowy podłączenia układu przedstawiono na rys. 11. 13
Rysunek 11: Schemat ideowy podłączenia czujnika HP01S (źródło [9] Tabela 3: Podstawowe parametry układu DB18B20 Temperatura pracy -55... 125 C Tolerancja + 0.5% dla zakresu -40... 125 C Zakres pomiarowy -55... 125 C Zakres napięć zasilajacych DC 3... 5.5V Wyprowadzenie danych Cyfrowe (1-wire) 12-bitowe Uwagi Czujnik energooszczędny 3.3 DS18B20 - pomiar temperatury DB18B20 jest 12 bitowym czujnikiem temperatury o niewielkiej dokładności, wykorzystanym w celu określenia temperatury układów elektronicznych znajdujących się wewnątrz obudowy stacji meteorologicznej. Może to pomóc w diagnostyce elementów elektronicznych podczas pracy w skrajnych warunkach temperaturowych. Podstawowe parametry czujnika DS18B20 przedstawiono w tabeli 3 3.4 TCRT5000 - transoptor odbiciowy TCRT5000- transoptory odbiciowe zamontowane w deszczomierzu i wiatromierzu. Zależnie od ilości odbitego światła tranzystory wbudowane w czujnik przewodzą coraz większy prąd. Wraz z spadkiem optotranzystora, Z dzielnika napięcia stworzony z niego i dołączonego rezystora będziemy otrzymywali coraz wyższe napięcia. W przypadku przekroczenia napięcia z potencjometru, komparatory będące wzmacniaczami operacyjnymi będą pokazywały napięcie VCC. W innym przypadku GND (Szczegóły w dokumentacji układu Rysunek 12: Schemat ideowy podłączenia czujnika DB18B20 (żródło [11]) 14
Rysunek 13: Schemat ideowy podłączenia czujników TCRT5000. Układ zastosowano w deszczomierzu. [10]). 3.5 Wiatromierz Wiatromierz składa sie z dwóch zasadniczych części: wykrywania prędkości wiatru oraz wykrywania kierunku wiatru. Opisany zostanie tutaj układ wykrywania kierunku wiatru. Układ wykrywania kierunku obrotu wiatromierza wykonano w oparciu o dwa scalone mostki, których napięcie wyjściowe zależy od kierunku i natężenia pola magnetycznego, w którym układy te się znajdują. Wykorzystano do tego celu łatwo dostępne i odznaczające się wystarczającą precyzją układy KMZ10B firmy Philips. Schemat wewnętrzny mostka przedstawiono na rys.14 Zasilanie podłączone jest między zaciski GND oraz VCC, natomiast wyjście z mostka stanowią zaciski +VO oraz VO. Układ zasilany jest niesymetrycznym napięciem stabilizowanym 5V. Sygnał wyjściowy, ze względu na niewielką amplitudę wymaga odpowiedniego wzmocnienia. Do tego celu wykorzystano wzmacniacz pomiarowy, którego schemat przedstawiono na rys. 15 Wzmocnienie układu dane jest wzorem 4: ( k Uf = 1 + 2R 1 R 2 ) R4 R 3 (4) Wzmocnienie układu regulowane jest za pomocą rezystora R1 (wstawiono w to miejsce precyzyjny wieloobrotowy potencjometr montażowy). Wartości rezystancji równe są 15
Rysunek 14: Schemat wewnętrzny układu KMZ10B; schemat pochodzi z karty katalogowej producenta Rysunek 15: Schemat ideowy wzmacniacza pomiarowego, wykorzystanego do kondycjonowania sygnału wyjściowego z mostka 16
Rysunek 16: Schemat ideowy zasilacza układu kondycjonera oraz mostka pomiarowego odpowiednio: ˆ R 1 potencjometr 4.7k ˆ R 2 10k ˆ R 3 4.7k ˆ R 4 22k Wzmacniacz pomiarowy z rys. 15 zbudowano w oparciu o układ scalony TL074, zawierający w swej strukturze 4 wzmacniacze operacyjne. Zastosowanie poczwórnego wzmacniacza pozwala zmniejszyć wpływ zmian takich parametrów m. in. jak temperatura, na działanie układu. Układ na podawać na wyjście sygnał unipolarny o zmiennej wartości jest to podyktowane zastosowanym przetwornikiem A/C, który przyjmuje na swoje wejście tylko sygnały unipolarne (dodatnie). Oprócz tego, układ zasilany jest pojedynczym napięciem, ze względu na unifikację budowy zasilacza wymagany jest wówczas tylko jeden zestaw akumulatorów rezerwowych. Kompletny schemat wzmacniacza pomiarowego przedstawiono na rys.16 Potencjometrem R 1 ustala się wzmocnienie wzmacniacza pomiarowego, rezystorem R 2 ustala się wartość podkładu stałego czyli 0 dla przetwornika A/C. Układ jest tak wyregulowany, że na wyjściu w stanie spoczynku panuje napięcie stałe równe połowie napięcia zasilania przetwornika A/C. Pojedynczy układ KMZ10B umożliwia jednoznaczne określenie kątów z zakresu 0; 180 stopni. Zatem, aby móc określić jednoznacznie kąty z zakresu 360 stopni, potrzebne jest użycie dwóch mostków, ustawionych prostopadle do siebie. Mostki te ustawione są w pobliżu magnesu trwałego, umieszczonego na osi, na której zamocowany jest wskaźnik kierunku wiatru. 3.6 Deszczomierz Jednym z podstawowych danych meteorologicznych jest wielkość opadów. Podstawowym założeniem naszej stacji była bezobsługowość i bezawaryjność. Z tego powodu nie mogliśmy użyć najpopularniejszego rodzaju deszczomierza- deszczomierza Hellmanna- pomiar 17
Rysunek 17: Deszczomierz Hellmann (źródło: [7] ilości wody w zbiorniku jest skomplikowany mechanicznie, mechanizm wylewania wody ze zbiornika mógłby być natomiast awaryjny (rys.17 ). Użytą przez nas konstrukcją jest deszczomierz korytkowy. Przykładowa konstrukcja przedstawiona jest na rys.18. Urządzenie zlicza przerzuty korytka. Mnożąc to przez ilość wody potrzebną na zmianę położenia korytka można obliczyć ilość wody, która spadła na powierzchnię lejka. Chcieliśmy ustrzec się awarii polegających na zaśniedzeniu stykówzastosowaliśmy pomiar optyczny transoptorami odbiciowymi. Schemat naszej konstrukcji przedstawiony jest na rys. 19. Użyte przez nas podpórki mają znaczenie praktyczne. Pozwalają na bezproblemowe wyrównywanie pojemności potrzebnych na poszczególne przerzuty. Niech V p będzie objętością potrzebną w korytku, aby ten zmienił swoje położenie. Podczas tworzenia korytka, nie zadaliśmy oczekiwanej objętości V p. Chcieliśmy, aby było możliwie małe. Z tego powodu zostało wykonane z cienkiej stali nierdzewnej. Krawędzie korytka zostały ścięte w celu obniżenia środka ciężkości konstrukcji. Uzyskany przez nas deszczomierz ma Vp na poziomie 1ml. Porównując ten wynik z produktami komercyjnymi, np. deszczomierzami ARG100 i RG50 okazuje się że uzyskaliśmy dobry wynik, na poziomie lepszego RG50 (patrz [6]). W naszym przypadku dysponowali będziemy mniejszą powierzchnią zbierającą, więc uzyskana rozdzielczość będzie się zawierać pomiędzy tymi produktami. ( 0,1mm-0.2mm ). Problemem w przypadku deszczomierza jest fakt, że z korytka nie spływa cała woda. Doświadczenia pokazują jednak, że ten fakt ma wpływ jedynie na drugi przerzut ( przy całkowicie wyschniętym korytku). Pierwszy przerzut ma 1ml, kolejny mniej( ok. 0.7ml) a następnie powrotem 1ml. Wynika to z tego, że po obu stronach korytka pozostaje podobna ilość wody i jest to powtarzalne. 18
Rysunek 18: Przykładowa realizacja deszczomierza korytkowego (źródło: [8] Rysunek 19: Rysunek użytego deszczomierza korytkowego 19
Rysunek 20: Najważniejsze siły wpływające na obrót korytka Pomiary wykazały że pojemność potrzebna do wykonania jednego przerzutu deszczomierza wynosi 1ml. Rura deszczomierza ma natomiast średnicę 11cm. Wynika z tego, że jeden przerzut odpowiada opadom: h = 1ml π r = 1cm 3 2 π (5.5cm) = 1cm 3 2 95, 033cm = 1cm 3 = 0, 0105cm = 0, 105mm (5) 2 95, 033cm2 Opis fizyczny korytka: V p - objętość potrzebna do przerzutu α - kąt ułożenia korytka F m = m g siła ciężkości samego korytka F w = V p ρ g siła ciężkości wody F d - siła pochodząca od zmiany pędu spadających kropel l m = h sin(α) odległość wektora F m od środka obrotu l w odległość środka ciężkości wody znajdującej się w korytku Siła F d pochodzi od zmiany momentu spadającej kropli. Ze względu na to, że przecina ona oś obrotu, nie wytwarza momentu. Dzięki temu pojemność Vp nie zależy od intensywności opadów. Moment przerzutu jest równoznaczny z równowagą momentów działających na korytko, czyli: F m l m = F w l w 20
Rysunek 21: Przebieg komunikacji1 [2] 4 Program 4.1 Obsługa czujnika SHT71 Opis rozpoczynamy od obsługi czujnika wilgotności i temperatury SHT71[2] Komunikacja z czujnikiem jest możliwa za pomocą dwóch przewodów (szyna zegara i danych), lecz nie jest to typowe I2C Przebieg komunikacji zaprezentowany jest na rys. 21. Do komunikacji potrzebny jest jeden pin(zegar) o wyjściu Push-Pull, oraz drugi pin(dane) o wyjściu otwary dren(stan wysoki nie wpływa na przewód, natomiast stan niski wymusza potencjał masy). Z tego powodu pin zegara jest typowym wyjściem, natomiast pin danych jest ustawiony na stan niski. Jednakże aby wymusić stan wysoki zmieniana jest funkcjonalność pinu z wyjścia na wejście. Dokładniejszy opis zawarty jest w dokumentacji mikrokontrolera[1]; Aby ułatwić wykonywanie powyższych operacji, napisane zostały makra: #define SHTPort PORTD #define SHTDir DDRD #define SHTPin PIND #define SHTDat (1<<6) #define SHTSck (1<<7) #define SHTSetSck SHTPort = SHTSck #define SHTSetDat SHTDir&=(~SHTDat); #define SHTResSck SHTPort&= (~SHTSck) #define SHTResDat SHTDir =SHTDat; #define SHTCheckDat (SHTPin&SHTDat) Natomiast inicjalizacja wygląda następująco: void InitSHT(void) SHTDir =SHTSck; SHTDir&=(~SHTDat); SHTPort&= (~SHTDat); 21
SHTSetDat; SHTResSck; _delay_ms(11); Pierwszym z wysyłanych przebiegów jest sygnał startu(rys. 21). Polega on na wysłaniu dwóch impulsów na szynie zegara, oraz zmianie wartości szyny danych w czasie gdy stan zegara jest wysoki: void SHT_Start(void) //Start SHTSetSck; _delay_us(100); SHTResDat; _delay_us(100); SHTResSck; _delay_us(100); SHTSetSck; _delay_us(150); SHTSetDat; _delay_us(100); SHTResSck; Kolejnym wykorzystywanym sygnałem jest reset. W przypadku gdy wystłpi błąd komunikacji naleąy wygenerować co najmniej 10 taktów zegara: char ii; SHTSetDat; for(ii=0;ii<10;ii++) SHTSetSck; _delay_us(10); SHTResSck; _delay_us(10); Nastepnym etapem jest wysyłanie danych. Wysyłanie kolejnych bitów polega na ustawieniu odpowiedniej wartości na szynie danych, oraz wykonaniu taktu zegara. Wysłanie pojedynczego bajtu zrealizowano następująco: for(ii=0;ii<8;ii++) // _delay_us(50); if(byte&(1<<(7-ii)))shtsetdat; else SHTResDat; _delay_us(100); SHTSetSck; //sprawd[u+fffd] co ustawi[u+fffd] 22
_delay_us(100); SHTResSck; Poniżej przedstawiono konkretny przykład przesyłania informacji - odczyt temperatury. Komunikacja rozpoczyna się od wysłania sygnału startu. Następnie wysłana jest komenda, oraz sprawdzane jest potwierdzenie ze strony czujnika: SHT_Start(); char ii; char comand= 0b00000011; //zmierz temperature //_delay_us(50); for(ii=0;ii<8;ii++) // _delay_us(50); if(comand&(1<<(7-ii) ) )SHTSetDat; else SHTResDat; _delay_us(100); SHTSetSck; _delay_us(100); SHTResSck; //_delay_us(50); SHTSetDat; _delay_us(50); SHTSetSck; _delay_us(50); if(!shtcheckdat)return ERROR; SHTResSck; _delay_us(50); Po tej komendzie dokonywany jest pomiar temperatury. W tym czasie czujnik wymusza stan niski na szynie danych. Należy czekać, aż szyna powróci do stanu wysokiego i rozpocząć odczyt danych z dokonanego pomiaru. Przy odbiorze danych szyna zegara jest taktowana, natomiast odczyt kolejnych bitów zachodzi w momencie, gdy stan szyny zegara jest wysoki. Następnie, aby móc kontynuować komunikację konieczne jest wygenerowanie sygnału potwierdzenia: while(shtcheckdat); // if(!shtcheckdat)lcd_writetext(")"); unsigned char w1=0,w2=0; for(ii=0;ii<8;ii++) SHTSetSck; if(shtcheckdat)w1 =(1<<(7-ii)); _delay_us(10); SHTResSck; 23
Rysunek 22: Sposób interpretacji odczytanych wartości([2]) _delay_us(10); SHTResDat; SHTSetSck; _delay_us(10); SHTResSck; SHTSetDat; _delay_us(10); Następnie w analogiczny sposób odczytywany jest drugi bajt danych. Jedyną różnicą jest nie wysyłanie sygnału potwierdzenia: for(ii=0;ii<8;ii++) SHTSetSck; if(shtcheckdat)w2 =(1<<(7-ii)); _delay_us(10); SHTResSck; _delay_us(10); Funkcja zwraca odczytaną wartość w formie 16 bitowej liczby całkowitej: return ((((int)w1)<<8)+w2); W analogiczny sposób wykonywany jest odczyt wilgotności. Należy następnie zinterpretować dane. Wykorzystane zostaną do tego wzory i tabele z rysunku 22. Korzystamy z 5V zasilania, oraz najwyższej rozdzielczości pomiarów. Korzystamy zatem z wartości współczynników: #define SHTD1-40 #define SHTD2 0.01 #define SHTC1-2.0468 #define SHTC2 0.0367 #define SHTC3-0.0000015955 #define SHTT1 0.01 #define SHTT2 0.00008 Natomiast obliczanie wartości temperatury i wilgotności wygląda następująco: 24
void SHT_read(float* t,float* h) int t_base=sht_read_t(); int h_base=sht_read_h(); *t=shtd1+shtd2*t_base; *h=shtc1+shtc2*h_base+shtc3*h_base*h_base; *h=*h+(*t-25)*( SHTT1+SHTT2*h_base); 4.2 Określanie kierunku wiatru Kolejnym zadaniem stacji meteorologicznej jest wyznaczanie kierunku wiatru. Wymagany jest enkoder absolutny. Do tego celu wykorzystano dwa czujniki magnetyczne ustawione do siebie prostopadle, natomiast pomiędzy nimi umieszczono magnes. Wyjścia wzmacniaczy pomiarowych podłączone są do wejść przetworników analogowo cyfrowych mikrokontrolera. Konfiguracja przetwornika wygląda następująco: SFIOR=0; //Free runing mode ADMUX = _BV(MUX2) _BV(MUX0) _BV(ADLAR); //kanał 3 ADCSRA = _BV(ADEN) _BV(ADPS1) _BV(ADSC);//uruchomienie ADC Pierwsza linijka włącza Fee runing mode. Kolejna kontroluje multiplekser i sposób odczytu danych. Przetwornik zostanie włączony, i rozpoczęty został odczyt. Kolejna linijka decyduje o odczytu wartości z kanału 3, oraz o reprezentacji wyniku wyjustowanego do lewej strony. Ostatnia z linijek ustawia szybkość odczytu, włącza przetwornik, oraz rozpoczyna konwersję. Podczas pomiaru, odczytywane są kolejno napięcia z analogowego wejścia 2 i 3. Odczytane wartości należy wycentrować. Wirtualna masa wzmacniaczy pomiarowych ustawiona jest na 2.5V. W związku z tym wartości są wycentrowane względem wartości 128 (połowa zakresu). Ostatnim krokiem jest wykorzystanie funkcji atan2. wynik1=adch; ADMUX ^= _BV(MUX0); _delay_ms(10); wynik2=adch; ADMUX ^= _BV(MUX0); float kierunek=180/3.1415*atan2(wynik1-128,wynik2-128); 4.3 Obsługa PCF8583 Nastepne zagadnienie to obsługa zegara czasu rzeczywistego PCF8583. Pominięte zostaną szczegóły dotyczące komunikacji za pośrednictwem I2C a jedynie omówione zostaną najważniejsze funkcje - wysyłania i odbioru danych(szczegóły można znaleźć w [1] oraz [4]). Funkcja odbierania danych. Funkcja przyjmuje parametry addr- adres urządzenia slave, poz - pozycja rejestru w pamięci urządzenia slave. Wskaźnik buff jest tablicą, w której zapisywane będą dane, natomiast size jest liczbą bajtów do odczytania: void I2C_odbierz(uint8_t *buff,uint8_t addr,uint8_t poz,uint8_t size) 25
Rysunek 23: Ułożenie rejestrów w pamięci PDB8583. Przykład rejestrów. źródło: [3] I2C_start(); //sygnał startu I2C_write(addr); //wysyłanie adresu jako nadawca I2C_write(poz); //pozycja w pamcięci RTC I2C_start(); //restart I2C_write(addr 1); //adres jako odbiorca uint8_t i; for(i=0;i<size;i++) buff[i]=i2c_read((i==size-1)?1:0); //potwierdzenie, jeśli nie ostatni I2C_stop(); //sygnał stopu Podobnie wygląda zapis danych do urządzenia slave: void I2C_wyslij(uint8_t *buff,uint8_t addr,uint8_t poz,uint8_t size) I2C_start(); //sygnał startu I2C_write(addr); //wysyłanie adresu jako nadawca I2C_write(poz); //pozycja w pamięci RTC uint8_t i; for(i=0;i<size;i++) I2C_write(buff[i]);//wyślij dane I2C_stop(); //sygnał stopu Analizując rysunek 23 można zauważyć, że potrzebne będzie makro, zamieniające liczby zapisane jako cyfra czterobitowa, na liczbę dziesiętną. Makra zamiany z czterobitowej 26
notacji, dziesiętną i na odwrót, wyglądają następująco: #define hex2dec(a) ((a&0x0f)+(a&0xf0)/16*10) #define dec2hex(a) ((a%10)+(a/10)*16) Wykorzystując te makra i powyższe funkcje można w prosty sposób odczytać datę i czas: I2C_odbierz(wynik,PCF_8583_addr,2,6); czas_lokalny.sek=hex2dec(wynik[0]); czas_lokalny.min=hex2dec(wynik[1]); czas_lokalny.godz=hex2dec(wynik[2]); czas_lokalny.dzien=hex2dec(wynik[3]&0x3f); czas_lokalny.mies=hex2dec(wynik[4]&0x1f); Zmienna czas_lokalny jest pomocniczą strukturą. W ostatnich 2 wierszach jest założona dodatkowa maska. Wynika to z dodatkowej, nie wykorzystywanej przez nas zawartości rejestrów(rys. 23). Ustawienie zegara czasu rzeczywistego wygląda analogicznie. Zostanie to przedstawione w następnym rozdziale. 4.4 Komunikacja UART W projekcie do komunikacji stacji z komputerem zastosowano protokół UART. Jego największą zaletą jest popularność i łatwość modyfikacji. W języku C# został napisany program do obsługi stacji- zapisywania odbieranych danych oraz ustawiania zegara. 4.4.1 Obsługa ze strony mikrokonrolera Obsługę zaczynamy od inicjalizacji: #define RS_BAUD 9600 #define RS_UBRR F_CPU / 16 / RS_BAUD - 1 void uart_init(uint16_t ubrr) // Ustawienie prędkości transmisji UBRRH = (uint8_t)(ubrr >> 8); UBRRL = (uint8_t)ubrr; // Włączenie nadajnika i odbiornika UCSRB = (1 << RXEN) (1 << TXEN) (1<<RXCIE); // Ustawienie formatu ramki: // 8 bitów danych, 1 bit stopu, brak parzystości UCSRC =(1<<7) (1 << UCSZ0) (1 << UCSZ1); Ustawiona została prędkość przesyłu danych. Uruchomiony kanał nadawczy i odbiorczy, oraz przerwanie przy odbieraniu danych. Funkcje wysłania znaku i ciągu znaków: 27
void uart_putc(char data) if(data == \n ) uart_putc( \r ); // Oczekiwanie na zakończenie nadawania while (!(UCSRA & (1 << UDRE))); // Wysłanie danych UDR = data; void uart_puts(const char *s ) while (*s) uart_putc(*s++); Następuje tu również konwersja znaku końca linii na wersję akceptowaną przez system Windows. Dane wysyłane są jako ciąg znaków. Do konwersji liczb wykorzystano funkcję sprintf. Odbieranie danych odbywa się w procedurze obsługi przerwania. Odebrane dane są ciągiem liczb 8 bitowych bez znaku. Na początku znajduje się atrybut informujący o typie paczki. Kodowanie pierwszego bajtu wygląda następująco: #define _set_time_id 0 #define _set_interval_id 1 #define _presence_id 2 Jak widać zaimplementowane zostały 3 typy odbieranych danych. Pierwszy z nich to dane konieczne do ustawienia zegara czasu rzeczywistego, kolejny to dane konieczne do zmiany częstotliwości pomiarów. Ostatni rodzaj paczki to świadectwo obecności odbiorcy. Natomiast na końcu ramki znajduje się znacznik końca paczki 0xFF. Znacznik też nie może być interpretowany jako dane. W przypadku pierwszego z typu przesyłanych danych, odebrane dane są datą i godziną do ustawienia. Więcej szczegółów dotyczących ustawienia zegara RTC znaleźć można w rozdziale Obsługa PCF8583. Zmienna Rx_poz jest wskaźnikiem pozycji w buforze odbiorczym. Po wykryciu znacznika końca paczki następuje interpretacja i ustawienie zegara RTC. ISR (USART_RXC_vect) Rx_buff[Rx_poz] = UDR; if(rx_buff[rx_poz]==0xff) if(rx_poz==7) if(rx_buff[0]==_set_time_id) //Ustawianie zegara RTC RTC_conf_data[0]=dec2hex(Rx_buff[1]); RTC_conf_data[1]=dec2hex(Rx_buff[2]); 28
RTC_conf_data[2]=dec2hex(Rx_buff[3]); RTC_conf_data[3]=dec2hex(Rx_buff[4])+((Rx_buff[6]%4)<<6); RTC_conf_data[4]=dec2hex(Rx_buff[5]); I2C_wyslij(RTC_conf_data,PCF_8583_addr,2,5); // PCF_odbierz(&czas_lokalny); // nastepny_pomiar=time2sec(&czas_lokalny); Na szczególną uwagę zasługuje 3. wiersz od dołu. W bajcie Rx_buff[6] znajduje się obecny rok pomniejszony o 2000. Na najstarszych bitach modyfikowanego tu rejestru RTC ma znajdować się reszta z dzielenia bieżącego roku przez 4(rys. 23). Jest to istotne, aby prawidłowo ustawić RTC. Dzięki takiemu zabiegowi zegar uwzględnia rok przestępny. W dalszej części przerwania znajduje się interpretacja paczek dotyczących ustawiana częstotliwości pomiarów, oraz paczki odpowiadającej za ustawienie flagi obecności odbiorcy: else if(rx_buff[0]==_set_interval_id) //Ustawianie częstotliwości pomiarów okres_pomiaru=(((uint16_t)rx_buff[2])<<8) + Rx_buff[1]; else if(rx_buff[0]==_presence_id) //Ukazanie obecności odbiorca_obecny=1; Rx_poz=0; else Rx_poz++; 4.4.2 Program odbierający dane Napisany został własny terminal do obsługi stacji meteorologicznej(rys. 24). Umożliwia on odbiór danych i zapis ich do pliku. Terminal wysyła również sygnał obecności, umożliwia ustawienie zegara RTC, oraz modyfikację czasu pomiędzy kolejnymi pomiarami. Do komunikacji użyto kontrolkiserialport1. Przy ustanawianiu połączenia sprawdzane jest to, czy nie jest ono już ustanowione, oraz otwierany jest plik tekstowy gdzie zostaną zapisane dane. Są one dopisywane do pliku. private void button1_click(object sender, EventArgs e) if (serialport1.isopen) serialport1.close(); serialport1.baudrate = 9600; serialport1.portname = combobox1.text; serialport1.open(); timer1.enabled = true; sw = new StreamWriter("log.txt", true); sw.writeline("nawiązano połączenie"); sw.flush(); //czysci bufor, wszystko co bylo w buferze zostaje zapisane do 29
Rysunek 24: Widok programu do komunikacji ze stacją meteorologiczną 30
Dane odbierane z taktem timera. Odbiera on wszystkie dostępne dane, a następnie wypisuje je na ekran. Zapisuje również ten ciąg znaków do pliku. private void timer1_tick(object sender, EventArgs e) if (serialport1.isopen) String s = serialport1.readexisting(); textbox1.appendtext(s); sw.write(s); sw.flush(); Do ustawienia zegara czasu rzeczywistego w stacji meteorologicznej najwygodniej użyć zegara systemowego Windows. Aby móc skorzystać z funkcji GetSystemTime trzeba ją zaimportować z systemowego pliku dll. W tym celu potrzebne jest struktura do której będą zapisane dane. Następnie możemy wczytać funkcję z pliku dll. public struct SYSTEMTIME public ushort wyear; public ushort wmonth; public ushort wdayofweek; public ushort wday; public ushort whour; public ushort wminute; public ushort wsecond; public ushort wmilliseconds; [DllImport("kernel32.dll")] public extern static void GetSystemTime(ref SYSTEMTIME lpsystemtime); Po kliknięciu na przycisk ustawienia czasu sprawdzane jest, czy komunikacja jest otwarta. Jeśli tak, odczytujemy czas systemowy. Odczytany czas jest czasem GMT. Należy dodatkowo przeliczyć go na czas w Polsce. W czasie letnim jest to GMT, a w czasie zimowym GMT +1. W związku z tym, jeśli odznaczony jest czas letni, dodawana jest dodatkowa godzina. Jeśli okaże się, że rozpoczął się kolejny dzień, to zmieniany jest dzień i zerowana jest godzina. Algorytm nie jest kompletny, ponieważ Nie ma uwzględnionego przypadku przejścia do nowego miesiąca, jednak jest mało prawdopodobne że ktoś będzie chciał ustawiać zegarek stacji meteorologicznej o tak nietypowej porze. private void button2_click(object sender, EventArgs e) if (serialport1.isopen) SYSTEMTIME st = new SYSTEMTIME(); GetSystemTime(ref st); 31
if (!checkbox1.checked) st.whour++; if (st.whour == 24) st.whour = 0; st.wday++; //Błąd, jeśli jest zaraz po północy nowego dnia miesiąca. Następnie wysyłana jest informacja o czasie do stacji: byte[] Tx_buf = new byte[8]; Tx_buf[0] = (byte)st.wsecond; Tx_buf[1] = (byte)st.wminute; Tx_buf[2] = (byte)st.whour; Tx_buf[3] = (byte)st.wday; Tx_buf[4] = (byte)st.wmonth; Tx_buf[5] = (byte)(st.wyear%100); Tx_buf[6] = 0xFF; serialport1.write(tx_buf,0,7); Kolejną funkcją programu jest zmiana okresu wykonywania pomiarów. Program odczytuje z pola tekstowego liczbę całkowitą. Wysyłana jest ona do Stacji jako 2 liczby 8 bitowe. Ze względu na to, że dla poprawnej komunikacji, żadna z wysyłanych wartości nie powinna wynosić 0xFF, sprawdzany zostaje ten warunek i w innym przypadku wartości, zostają zmodyfikowane o jeden w dół. 4.5 Obsługa deszczomierza Deszczomierz posiada 2 czujniki sygnalizujące położenie korytka. Dzięki temu można zminimalizować liczę błędów spowodowanych odbiciem korytka od podstawki(może się zdażyć, że po przewrocie korytka, te odbije się lekko). Chcemy również by deszczomierz nie zarejestrował chwilowych błysków światła jako dodatkowych 2 przerzutów korytka. W związku z tym pamiętamy poprzedni stan i jeżli zmieni się on na pośredni, a następnie znowu na ten sam, nie zaliczamy go do przerzutu korytka. Wyróżniamy więc 4 następujące po sobie stany: /* * stan 0 -- (Deszcz_lewa && Deszcz_prawa) (!Deszcz_lewa &&!Deszcz_lewa) * stan 1 / (Deszcz_lewa &&!Deszcz_prawa) stan 2 -- (Deszcz_lewa && Deszcz_prawa) (!Deszcz_lewa &&!Deszcz_lewa) * stan 3 \ (!Deszcz_lewa && Deszcz_prawa) */ Po prawej stronie napisane są warunki, aby zaliczyć ułożenie do tego stanu. Wykorzystane są tu makra: #define Deszcz_lewa (PIND&(1<<2)) #define Deszcz_prawa (PIND&(1<<3)) 32
Rozpoznają one stan pinów podłączonych do czujników. Dodatkowo należy rozróżnić stan 0 od stanu 2. Wykorzystuje się do tego informację z poprzedniego stanu. Cały algorytm wygląda następująco: if( ((Deszcz_lewa && Deszcz_prawa) (!Deszcz_lewa &&!Deszcz_lewa))&&(deszczomierz_ deszczomierz_stan=(deszczomierz_stan+1)%4; if(deszcz_lewa &&!Deszcz_prawa) if(deszczomierz_stan==0) deszczomierz_licznik++; deszczomierz_stan=1; if(!deszcz_lewa && Deszcz_prawa) if(deszczomierz_stan==2) deszczomierz_licznik++; deszczomierz_stan=3; Wykonywany on jest w przerwaniach zewnętrznych, które są wykonywane zarówno przy narastającym, jak i opadającym zboczu. Na poczatku działania programu konfigurowane są więc przerwania i odczytywany początkowy stan deszczomierza: //MCUCR =(1<<ISC10) (1<<ISC00);//zmiana logiczna na INT0 lub INT1 powoduje przerwanie. //GICR = (1<<INT0) (1<<INT1); //zezwolenie na przerwania od IN0 i INT1 PORTD =(1<<2) (1<<3); // podciągnij DDRD&=~((1<<2) (1<<3)); //wejście //stan początkowy: if(deszcz_lewa &&!Deszcz_prawa) deszczomierz_stan=1; else if(!deszcz_lewa && Deszcz_prawa) deszczomierz_stan=3; else deszczomierz_stan=0; Pomiary wykazały że pojemność potrzebna do wykonania jednego przerzutu deszczomierza wynosi 1ml. Rura deszczomierza ma natomiast średnicę 11cm. Wynika z tego, że jeden przerzut odpowiada opadom: h = 1ml π r = 1cm 3 2 π (5.5cm) = 1cm 3 2 95, 033cm = 1cm 3 = 0, 0105cm = 0, 105mm (6) 2 95, 033cm2 Opady obliczamy więc w prosty sposób: 33
float deszcz_mm = deszczomierz_licznik*0.105; Wielkość opadów jest zerowana ze zmianą dnia. Może się to również odbyć w przypadku zmiany ustawień zegara. if(poprzedni_dzien!=czas_lokalny.dzien) poprzedni_dzien=czas_lokalny.dzien; deszczomierz_licznik=0; nastepny_pomiar=0; //reset czaszu następnego pomiaru 4.6 Makroskopowy algorytm działania programu Mając zaimplementowaną obsługę kilku przyrządów pomiarowych można zająć się algorytmem sterującym całą stacją meteorologiczną. Podstawowym założeniem było dokonywanie pomiarów w stałych, zadanych odstępach czasu. W przypadku obecności Odbiorcy mają one być bezpośrednio przesyłane. W innym przypadku sś one zapisywane do pamięci mikrokontrolera. Pamięć, w której będą składowane pomiary jest tymczasowo pamięć RAM. Ze względu na małą pojemność możliwe jest przechowanie tylko nie wielkiej liczby pomiarów (ok. 100). Dalszy rozwój stacji wymaga dodania obsługi pamięci zewnętrznej, np. kart pamięci SD. Algorytm działania programu przedstawiony jest na rys.25. Przejdźmy do implementacji przedstawionego algorytmu. W pierwszej kolejności należało stworzyć strukturę przechowującą dane z janego pomiaru. Aby ograniczyć miejsce w pamięci zrezygnowano z typu float. Pomiary są przechowywane w 16 bitowych liczbach całkowitych. Struktura wygląda następująco: struct paczka_danych struct data czas; int16_t kierunek_wiatru; //kąt 2 0,1stopnia int16_t wilgotnosc; //procenty/256 int16_t temperatura; //stopnie Celsjusza/256 uint16_t cisnienie; //obecnie brak uint16_t deszczomierz; //liczba przerzutów ; W komentarzach przedstawione są jednostki. Kolejnym podstawowym wymogiem było posiadanie kolejki FIFO. Idealny w tym zastosowaniu jest bufor cykliczny[5]. Na rysunku 26 możemy zauważyć, że do bufora potrzebne będą 3 elementy. Wskaźnik odczytu i zapisu( ogon i głowa ), oraz tablica w kórej ma się ona znajdować. Definicja elementów bufora: #define _Buff_Length 100 struct paczka_danych bufor_cykliczny[_buff_length]; uint16_t bufor_cykliczny_glowa=0; uint16_t bufor_cykliczny_ogon=0; 34
Rysunek 25: Schemat działania stacji meteorologicznej 35
Rysunek 26: Zasada działania bufora cyklicznego; źródło:[5] Nastśpnie należało w jakiś sposób określać odstępy między pomiarami. Zdecydowano się na przeliczanie czasu na sekundy które upłynęły od północy danego dnia. Przy każdym pomiarze dodawana jest do obecnego czasu zadana liczba sekund, dzięki czemu otrzymywany jest kolejny czas pomiaru. Przy wystąpieniu zadanego czasu dane dodawane są do bufora cyklicznego. W przypadku gdy nie ma miejsca na nowe dane, kasowane są najstarsze z nich: if(obecny_sec>=nastepny_pomiar) nastepny_pomiar=obecny_sec + okres_pomiaru; // // pomiary // if((bufor_cykliczny_glowa+1)%_buff_length == bufor_cykliczny_ogon) bufor_cykliczny_ogon++; //skasowanie najstarszego z pomiarów //warunek brzegowy bufora if(bufor_cykliczny_ogon==_buff_length)bufor_cykliczny_ogon=0; //Dane w odpowiednich jednostkach: bufor_cykliczny[bufor_cykliczny_glowa].czas=czas_lokalny; bufor_cykliczny[bufor_cykliczny_glowa].kierunek_wiatru=kierunek_wiatru; bufor_cykliczny[bufor_cykliczny_glowa].wilgotnosc=wilgotnosc; bufor_cykliczny[bufor_cykliczny_glowa].temperatura=temperatura; 36
bufor_cykliczny[bufor_cykliczny_glowa].cisnienie=0; bufor_cykliczny[bufor_cykliczny_glowa].deszczomierz=deszczomierz_licznik; bufor_cykliczny_glowa++; //warunek brzegowy bufora if(bufor_cykliczny_glowa==_buff_length)bufor_cykliczny_glowa=0; Ostatnim z elementów jest przesył danych. Dane wysyłane są jedynie wtedy, gdy jakies dane oczekują w kolejce do wysłania, i gdy jest podłączony Odbiorca.Pierwszy warunek jest równoznaczny z tym, że pozycja głowy różni się od pozycji ogona. Wysyłanie danych wygląda zatem następująco: if(bufor_cykliczny_glowa!=bufor_cykliczny_ogon && odbiorca_obecny==1) odbiorca_obecny=0; while(bufor_cykliczny_glowa!=bufor_cykliczny_ogon) //Deszczomierz: //rura 110mm, 1ml na jedno. 0,083 mm/takt float deszcz_mm_wyp = bufor_cykliczny[bufor_cykliczny_ogon].deszczomierz*0.105; float temp_wyp= bufor_cykliczny[bufor_cykliczny_ogon].temperatura/256.0; float wilg_wyp= bufor_cykliczny[bufor_cykliczny_ogon].wilgotnosc/256.0; float kier_wyp= bufor_cykliczny[bufor_cykliczny_ogon].kierunek_wiatru/10.0; struct data czas_wyp= bufor_cykliczny[bufor_cykliczny_ogon].czas; sprintf(tekst,"%d;%d;%d;%d;%d;%f;%f;%f\n",czas_wyp.mies,czas_wyp.dzien, czas_wyp.godz,czas_wyp.min,czas_wyp.sek,temp_wyp,wilg_wyp,deszcz_mm_wyp); uart_puts(tekst); bufor_cykliczny_ogon++; if(bufor_cykliczny_ogon==_buff_length)bufor_cykliczny_ogon=0; Taki sposób wysyłki ma swoje wady. Przy bardzo dużej ilości danych (przy obecności pamięci zewnętrznej) zgrywanie danych mogło by trwać dużo dłużej niż wynosi pojedynczy odstęp między pomiarami. W takim przypadku niektóre pomiary zostały by utracone. Aby tego uniknąć należało by zaimplementować komunikację z wykorzystaniem przerwań. 5 Testy 5.1 Wstępny test SHT71 Przeprowadzono kilka testów pomiaru temperatury i wilgotności czujnikiem SHT71. Pierwszym z nich jest krótki test w pomieszczeniu przedstawiający efekty krótkiego otwarcia okna(rys. 27). Widzimy nagły spadek wilgotności po jego otwarciu i dużo wolniejszy ubytek temperatury. Po zamknięciu okna temperatura powoli narasta do wcześniejszej wartości, natomiast wilgotność stabilizuje się na wartości znacznie mniejszej. Skok wilgotności po zamknięciu okna jest związany z mieszaniem się niewymienionego powietrza z nowym. 37
Rysunek 27: Dane z pomiaru w pomieszczeniu; zielonym kolorem- otwarcie okna; pomarańczowym- zamknięcie okna 5.2 Interface użytkownika poprzez stronę www Dane zebrane poprzez wyżej opisany program są zapisywane do pliku pomiary.txt znajdującego się w katalogu serwera www. Dane pomiarowe docelowo są przechowywane w bazie danych MySQL. Dzięki temu są łatwo dostępne z poziomu skryptu php oraz nie trzeba pisać programu do magazynowania wyników pomiarów. Niewątpliwą zaletą zastosowania serwera www dla interface u jest możliwość uzyskiwania informacji o warunkach pogodowych w jakich znajduje się stacja z dowolnego miejsca podłączonego do internetu i komputera wyposażonego w przeglądarkę. Dzięki zastosowania bazy MySQL otwiera się prostą drogę do rozbudowy interface u użytkownika lub budowy nowego, korzystającego z innej niż php technologii. Za graficzną reprezentację oraz dalsze przechowywanie pomiarów odpowiedzialne są następujące skrypty php: ˆ ˆ ˆ ˆ ˆ pobierz.php - skrypt sprawdzający, czy w pliku pomiary.txt znajdują się nowe pomiary oraz wpisujący je do bazy, index.php - uruchamia pobierz.php, wyświetla interface użytkownika, pozwala wybrać, zakres czasowy danych, a następnie prezentuje wykresy temperatury, opadu i wilgotności, wykrestemp.php, wykreswilg.php, wykresopad.php - skrypty odpowiedzialne za graficzną reprezentację odpowiednio: pomiarów temperatury, wilgotności i opadu. Została w nich użyta biblioteka jpgraph w wersji darmowej(patrz. [12]) config.php - zawiera informacje potrzebne do połączenia z bazą danych, tj. adres serwera MySQL, nazwa bazy, nazwa użytkownika oraz hasło, functions.php - plik zastosowany aby zachować przejrzystość kodu, zawiera funkcje do połączenia i komunikacji z bazą danych. 38
Rysunek 28: Wygląd interface u użytkownika w aplikacji php Gdy użytkownik wywoła w przeglądarce plik index.php, serwer uruchomi skrypt, który realizuje poniższy algorytm: 1. Sprawdź, czy w pliku pomiary.txt znajdują się informacje nt. pomiarów 2. Jeśli tak, przepisz te pomiary do bazy MySQL i usuń pomiary.txt 3. Wyświetl interface użytkownika 5.2.1 Plik pomiarów Pomiary zapisywane są przez program komunikujący się ze stacją wg poniższego szablonu: [miesiąc];[dzień];[godz];[min];[sek];[temp];[wilg];[opad][\n\r] Każdy pomiar znajduje się w nowej linii. Każda jest zakończona znacznikiem nowej linii [\n\r]. Skrypt parsujący pobierz.php zamienia znaczniki nowej linii na średniki, a następnie za pomocą funkcji explode() rozdziela uzyskany ciąg znaków tworząc tablicę. Znakiem rozdzielającym podanym jako parametr funkcji explode() jest ;. Ze względu na to, że stacja zapisuje dane więcej niż raz na sekundę, a taka częstość przy normalnej pracy nie jest potrzebna, skrypt pomija parametr [sek]. Baza została tak zaprojektowana, że identyfikatorem pomiaru jest data i godzina. Dlatego występuje konieczność znalezienia i usunięcia zdublowanych wpisów. Jest to następny krok. Tak przygotowana tablica pomiarów jest następnie wpisywana do bazy danych, skąd może zostać pobrana na życzenie użytkownika. 5.2.2 Pola wejściowe Formularz widoczny na rysunku 28 pozwala użytkownikowi wybrać przedział czasowy, z interesującymi pomiarami. Dzięki temu wykresy stają się czytelniejsze oraz można lepiej przyjrzeć się wybranym punktom. Po naciśnięciu Pokaż strona zostaje przeładowana, a dane z formularza są przekazywane do skryptów generujących wykresy za pomocą metody GET. 5.2.3 Wykresy Skrypty generujące wykresy używają biblioteki jpgraph. Jako parametry metody GET należy podać przedział czasowy. Odpowiednie pomiary są pobierane z bazy i przekazywane do metod obiektu utworzonego dzięki klasie z wyżej wspomnianej biblioteki. Każdy wykres jest automatycznie skalowany tak, aby był czytelny. Każdy punkt został opisany wartością, jaką reprezentuje. Wygląd wygenerowanych wykresów można zobaczyć na rysunkach 29, 39
Rysunek 29: Wykres wilgotności względnej w całodniowym teście systemu Rysunek 30: Wykres temperatury w całodniowym teście systemu 30 oraz 31. Na rysunku 32 widzimy wykres z rysunku 29 przy dużo gęstrzym ułożeniu punktów. Takie rozwiązanie daje większe pojęcie o zmianach mierzonych parametrów w czasie. Niestety w takim przypadku opis osi odciętych jest bardzo nieczytelna. Problem ten nie został jeszcze przez nas rozwiązany. 5.3 Całodniowy test systemu Wykonano całodniowy test systemu. Czujnik temperatury i wilgotności znajdował się za oknem, natomiast komputer zgrywał otrzymywane dane. Otrzymany plik był później interpretowany przez przedstawione powyżej skrypty. Na rysunkach 29 i 30 możemy oglądać uzyskane przebiegi temperatury i wilgotności. Pomiary były dokonywane z dużo większą częstotliwością niż jest to pokazane na tych rysunkach. Skrypt umożliwia przybliżanie interesujące nas obszary. Zostało to zademonstrowane na rysunku 31 40
Rysunek 31: Fragment wykresu wilgotności względnej w całodniowym teście systemu Rysunek 32: Wykres temperatury w całodniowym teście systemu, przy gęstym próbkowaniu 41