Stacja pogodowa która dokonuje pomiarów temperatury oraz wilgotności wewnątrz mieszkania oraz na zewnątrz. Stacja powstała bazując na artykule ze strony majsterkowo.pl użytkownika r.blaszczak Solarna stacja meteo z wykorzystaniem Wemos D1 mini Pro oraz Raspberry Pi 3 B+. Oczywiście projekt został zmodyfikowany zgodnie z własnym pomysłem.
Założenia projektu:
- pomiar temperatury i wilgotności wewnątrz i na zewnątrz
- zapisanie pomiaru na serwerze lokalnym
- wyświetlenie ostatniego pomiaru oraz historii w formie wykresu na stronie www
- zbudowanie aplikacji dla systemu Android
- wyświetlenie ostatniego pomiaru w formie widgetu dla Windows 10
Zasilanie stacji w odróżnieniu od opisanego w artykule zrealizowane z sieci energetycznej. Użyto żył z przewodu sieciowego (tzw. „skrętki”) po około 2m długości do połączenia sensorów z ESP8266.
Docelowo widok strony www na poniższym obrazie
Link do stronyWidok widgetu w Windows 10
Widok aplikacji Android
Wykorzystane części:
- Rsberry Pi 4 (wykorzystano wersję z 4 GiB RAM)
- ESP8266
- Czujnik wilgotności i temperatury DTH11
- Czujnik wilgotności i temperatury DTH22
Na Rasberry Pi instalujemy serwer www Apache z obsługą PHP oraz bazę danych, użyłem w projekcie MarinaDB. Ponadto warto zainstalować certyfikat SSL, w projekcie został użyty Let’s Encrypt do wygenerowania certyfikatu. Następnie tworzymy naszą bazę danych, w projekcie został użyty poniższy kod do utworzenia tabeli.
CREATE TABLE S_01
(id
int(20) NOT NULL AUTO_INCREMENT,timestamp
timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,temperatureIN
float NOT NULL,humidityIN
float NOT NULL,temperatureOUT
float NOT NULL,humidityOUT
float NOT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Kolejnym krokiem jest utworzenie w głównym katalogu Apache plików odpowiedzialnych za zapisywanie danych do bazy oraz wyświetlanie strony z danymi aktualnymi oraz wykresu z historią.
espdata.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php include('config.php'); $w_api_key = $_GET['api_key']; $table = $_GET['station_id']; $tin = $_GET['tIN']; $hin = $_GET['hIN']; $tout = $_GET['tOUT']; $hout = $_GET['hOUT']; $conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name); if ($w_api_key == $write_api_key ) { //w tym miejscu przygotowanie JSON i zapisanie do pliku $date = date('Y-m-d H:i:s', time()); $array = array('time' => $date,'tIN' => $tin,'hIN' => $hin,'tOUT' => $tout, 'hOut' => $hout); $fp = fopen('readmeteo.json', 'w'); fwrite($fp, json_encode($array, JSON_PRETTY_PRINT)); fclose($fp); //wysłanie zapytania do bazy danych - zapisujemy dane otrzymane z ESP8266 $result = mysqli_query($conn, "INSERT INTO $table(temperatureIN, humidityIN, temperatureOUT, humidityOUT) VALUES ($tin, $hin, $tout, $hout)"); if ($result === false){ echo "ERR"; $conn -> close(); } else { echo "OK"; $conn -> close(); } } else { echo "Niepoprawny API-KEY"; } ?> |
Strona www prezentująca aktualne dane oraz wykres historii.
meteochart.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
<?php include('config.php'); $w_api_key = $_GET['api_key']; $table = $_GET['station_id']; $dataPoints1 = array(); $dataPoints2 = array(); $dataPoints3 = array(); $dataPoints4 = array(); $conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name); if ($w_api_key == $write_api_key ) { $result = mysqli_query($conn, "SELECT * FROM $table ORDER BY id ASC"); if ($result === false){ echo "ERR"; $conn -> close(); } else { while ($row = mysqli_fetch_assoc($result)) { // Important line !!! Check summary get row on array .. foreach ($row as $field => $value) { // I you want you can right this line like this: foreach($row as $value) { switch ($field) { case "temperatureIN": $tIN_y = $value; break; case "temperatureOUT": $tOUT_y = $value; break; case "humidityIN": $hIN_y = $value; break; case "humidityOUT": $hOUT_y = $value; break; case "timestamp": $time = $value; break; } array_push($dataPoints1, array("label" => $time, "y" => $tIN_y)); array_push($dataPoints2, array("label" => $time, "y" => $tOUT_y)); array_push($dataPoints3, array("label" => $time, "y" => $hIN_y)); array_push($dataPoints4, array("label" => $time, "y" => $hOUT_y)); } } $conn -> close(); } }; ?> <!DOCTYPE HTML> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous"> <script> window.onload = function () { var chart = new CanvasJS.Chart("chartContainer", { theme: "light2", title: { text: "Historia temperatur, wilgotnoĹ›ci w domu i na zewnÄ…trz" }, subtitles: [{ text: "temperatura w stopniach celsjusza, wilgotność w %" }], legend:{ cursor: "pointer", itemclick: toggleDataSeries }, toolTip: { shared: true }, data: [{ type: "stackedArea", name: "Temperatura na zewnÄ…trz", showInLegend: true, visible: true, yValueFormatString: "#,##0 stopni Celsjusza", dataPoints: <?php echo json_encode($dataPoints2, JSON_NUMERIC_CHECK); ?> }, { type: "stackedArea", name: "Temperatura w domu", showInLegend: true, visible: true, yValueFormatString: "#,##0 stopni Celsjusza", dataPoints: <?php echo json_encode($dataPoints1, JSON_NUMERIC_CHECK); ?> }, { type: "stackedArea", name: "Wilgotność w domu", showInLegend: true, visible: true, yValueFormatString: "##,##0 ", dataPoints: <?php echo json_encode($dataPoints3, JSON_NUMERIC_CHECK); ?> }, { type: "stackedArea", name: "Wilgotność na zewnÄ…trz", showInLegend: true, visible: true, yValueFormatString: "##,##0 ", dataPoints: <?php echo json_encode($dataPoints4, JSON_NUMERIC_CHECK); ?> }] }); chart.render(); function toggleDataSeries(e){ if (typeof(e.dataSeries.visible) === "undefined" || e.dataSeries.visible) { e.dataSeries.visible = false; } else{ e.dataSeries.visible = true; } chart.render(); } } </script> </head> <body> <?php $w_api_key = $_GET['api_key']; $table = $_GET['station_id']; $conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name); if ($w_api_key == $write_api_key ) { $result = mysqli_query($conn, "SELECT * FROM $table where id = (select MAX(id) from $table)"); if ($result === false){ echo "ERR"; $conn -> close(); } else { echo "<div>"; while ($row = mysqli_fetch_assoc($result)) { // Important line !!! Check summary get row on array .. foreach ($row as $field => $value) { // I you want you can right this line like this: foreach($row as $value) { switch ($field) { case "temperatureIN": echo "<center><h1>Temperatura w domu</h1><h2><i class='fas fa-thermometer-half' style='color:#059e8a;'></i> ".$value." °C</h2></center>"; break; case "humidityIN": echo "<center><h1>Wilgotność w domu</h1><h2><i class='fas fa-tint' style='color:#00add6;'></i> ".$value." %</h2></center>"; break; case "temperatureOUT": echo "<center><h1>Temperatura na zewnÄ…trz</h1><h2><i class='fas fa-thermometer-half' style='color:#059e8a;'></i> ".$value." °C</h2></center>"; break; case "humidityOUT": echo "<center><h1>Wilgotność na zewnÄ…trz</h1><h2><i class='fas fa-tint' style='color:#00add6;'></i> ".$value." %</h2></center>"; break; case "timestamp": echo "<center><h1>Data i godzina pomiaru</h1><h2><i class='fas fa-clock'></i> ".$value."</h2></center>"; break; } } } echo "</div>"; $conn -> close(); } } else { echo "Niepoprawny API-KEY"; } ?> <div id="chartContainer" style="height: 370px; width: 100%;"></div> <script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script> </body> </html> |
Tworzenie wykresów za pomocą Canvasjs.com.
Następnie w podłączamy płytkę ESP8266 do komputera oraz uruchamiamy Arduino IDE, należy dodać obsługę płytki ESP8266 zanim będzie możliwość wgrania oprogramowania.
Kiedy mamy już dodaną płytkę do Arduino oraz wgrane dodatkowe biblioteki do obsługi WIFI oraz czujnika DTH możemy wgrać oprogramowanie na płytkę.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
#include <ESP8266WiFi.h> // [biblioteka wbudowana] ESP8266 WiFi #include "DHT.h" #include "config.h" // plik konfiguracyjny do edycji (musi być w tym samym folderze co niniejszy plik) WiFiClient client; // WiFi connection #define DHTPIN_1 4 // what digital pin the DHT22 is conected to #define DHTTYPE_DHT22 DHT22 // there are multiple kinds of DHT sensors #define DHTPIN_2 0 #define DHTTYPE_DHT11 DHT11 DHT dht_temp_hum_in(DHTPIN_2, DHTTYPE_DHT11); DHT dht_temp_hum_out(DHTPIN_1, DHTTYPE_DHT22); void setup() { Serial.begin(115200); // initialize the serial port pinMode(LED_BUILTIN, OUTPUT); // set builtin LED for output while (!Serial) { } // Wait dht_temp_hum_out.begin(); dht_temp_hum_in.begin(); logonToRouter(); // logon to local Wi-Fi } void loop() { postToRPi(); // send data to RPi delay(300000); } void logonToRouter() { String exitMessage = ""; int count = 0; WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { count++; // give up if more than 15 tries if (count > 15) { // display error code on serial monitor switch (WiFi.status()) { case 1: exitMessage = "Sieć Wi-Fi niedostępna lub\nStacja zbyt daleko od punktu dostępowego lub\nNiepoprawny SSID lub hasło lub\nAccess Poin nie pracuje na częstotliwości 2.4GHz."; break; case 2: // will never show this condition exitMessage = "Skanowanie sieci zakończone."; break; case 3: // will never show this condition exitMessage = "Połączono."; break; case 4: exitMessage = "Błąd połączenia."; break; case 5: exitMessage = "Utracono połączenie."; break; case 6: exitMessage = "Rozłączono."; break; } // switch Serial.print("WiFi fail: "); Serial.println(exitMessage); // retry after 1 minute } // if > 15 // otherwise if < 15 blink LED and wait 500ms before checking connection delay(500); // one-half second delay between checks Serial.print(""); } // while not connected // WiFi is sucesfully connected Serial.println(""); // new line to show IP address Serial.print("Połączono z siecią Wi-Fi. Otrzymany adres IP: "); Serial.println(WiFi.localIP().toString()); // is toString necessary? } // logonToRouter() void postToRPi() { // assemble and post the data if (client.connect(IOT_SERVER, IOT_SERVER_PORT) == true) { // Sensor readings float hOUT = dht_temp_hum_out.readHumidity(); float tOUT = dht_temp_hum_out.readTemperature(); float hIN = dht_temp_hum_in.readHumidity(); float tIN = dht_temp_hum_in.readTemperature(); Serial.println("Połączono z serwerem RPi."); // get the data to RPi client.print("GET /espdata.php?"); client.print("api_key="); client.print(write_api_key); client.print("&&"); client.print("station_id="); client.print(table_name); client.print("&&"); client.print("tIN="); client.print(tIN); client.print("&&"); client.print("hIN="); client.print(hIN); client.print("&&"); client.print("tOUT="); client.print(tOUT); client.print("&&"); client.print("hOUT="); client.print(hOUT); client.println(" HTTP/1.1"); client.println("Host: localhost"); client.println("Content-Type: application/x-www-form-urlencoded"); client.println("Connection: close"); client.println(); client.println(); Serial.println("Wysłano dane na serwer RPi."); } client.stop(); Serial.println("Rozłączono z serwerem RPi."); } |
Należy pamiętać aby w kodzie były podane odpowiednie piny do których zostały przylutowane czujniki.
Rainmeter
Widget do systemu Windows 10 zrealizowany przy zastosowaniu rainmeter do którego została opracowana wtyczka.
Kod wtyczki
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
[Metadata] Name=Stacja Meteo Author=Artur Kos Information=Email: R_E_D_O_X@wp.pl License=Free For All Version=1.0.0 [StacjaMeteo] Update=300000 Measure=WebParser URL=https://artur-kos.tplinkdns.com/readmeteo.json RegExp=(?siU)"time": "(.*)".*tIN": "(.*)".*hIN": "(.*)".*tOUT": "(.*)".*hOUT": "(.*)" [DateTime] Measure=WebParser URL=[StacjaMeteo] StringIndex=1 [Temperature_IN] Measure=WebParser URL=[StacjaMeteo] StringIndex=2 [Humidity_IN] Measure=WebParser URL=[StacjaMeteo] StringIndex=3 [Temperature_OUT] Measure=WebParser URL=[StacjaMeteo] StringIndex=4 [Humidity_OUT] Measure=WebParser URL=[StacjaMeteo] StringIndex=5 [MeterBackground] Meter=Image W=380 H=135 SolidColor=20,20,20,255 [DateTimeLabel] Meter=String X=5 Y=5 W=360 H=15 FontSize=11 FontColor=255,225,181,255 SolidColor=47,47,47,255 Padding=5,5,5,5 AntiAlias=1 Text=Data i godzina pomiaru: [MeterDateTime] Meter=String MeasureName=DateTime X=370 Y=5 W=300 H=15 FontSize=11 FontColor=252,251,202,255 SolidColor=0,0,0,1 Padding=5,5,5,5 StringAlign=Right AntiAlias=1 [TemperatureINLabel] Meter=String X=5 Y=30 W=360 H=15 FontSize=11 FontColor=255,225,181,255 SolidColor=47,47,47,255 Padding=5,5,5,5 AntiAlias=1 Text=Temperatura w domu (stopnie Celsjusza): [MeterTemperatureIN] Meter=String MeasureName=Temperature_IN X=370 Y=30 W=300 H=15 FontSize=11 FontColor=252,251,202,255 SolidColor=0,0,0,1 Padding=5,5,5,5 StringAlign=Right AntiAlias=1 [HumidityINLabel] Meter=String X=5 Y=55 W=360 H=15 FontSize=11 FontColor=255,225,181,255 SolidColor=47,47,47,255 Padding=5,5,5,5 AntiAlias=1 Text=Wilgotnosc w domu (%): [MeterHumidityIN] Meter=String MeasureName=Humidity_IN X=370 Y=55 W=300 H=15 FontSize=11 FontColor=252,251,202,255 SolidColor=0,0,0,1 Padding=5,5,5,5 StringAlign=Right AntiAlias=1 [TemperatureOUTLabel] Meter=String X=5 Y=80 W=360 H=15 FontSize=11 FontColor=255,225,181,255 SolidColor=47,47,47,255 Padding=5,5,5,5 AntiAlias=1 Text=Temperatura na zewnatrz (stopnie Celsjusza): [MeterTemperatureOUT] Meter=String MeasureName=Temperature_OUT X=370 Y=80 W=350 H=15 FontSize=11 FontColor=252,251,202,255 SolidColor=0,0,0,1 Padding=5,5,5,5 StringAlign=Right AntiAlias=1 [HumidityOUTLabel] Meter=String X=5 Y=105 W=360 H=15 FontSize=11 FontColor=255,225,181,255 SolidColor=47,47,47,255 Padding=5,5,5,5 AntiAlias=1 Text=Wilgotnosc na zewnatrz (%): [MeterHumidityOUT] Meter=String MeasureName=Humidity_OUT X=370 Y=105 W=300 H=15 FontSize=11 FontColor=252,251,202,255 SolidColor=0,0,0,1 Padding=5,5,5,5 StringAlign=Right AntiAlias=1 |
No Responses