/* D1MiniPro.ino
Wemos D1 Mini Pro - Stacja pogodowa zasilana panelem solarnym
Ustaw monitor portu szeregowego na 115,200 baud
Program opracowano na podstawie projektu Karl Berger (Berger Engineering) http://w4krl.com/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <Wire.h> // [biblioteka wbudowana] I2C bus
#include <BME280I2C.h> // [doinstaluj bibliotekę] Tyler Glenn https://github.com/finitespace/BME280
#include <BH1750.h> // [doinstaluj bibliotekę] https://github.com/claws/BH1750
#include <ESP8266WiFi.h> // [biblioteka wbudowana] ESP8266 WiFi
#include "config.h" // plik konfiguracyjny do edycji (musi być w tym samym folderze co niniejszy plik)
// *******************************************************
// *********************** GLOBALS ***********************
// *******************************************************
String unitStatus = ""; // for weather station status
// structure to hold sensor measurements in metric units
struct sensorData
{
float stationPressure; // measured station pressure in hPa
float seaLevelPressure; // calculated SLP
float temperature; // degrees Celsius
float humidity; // relative humidity %
unsigned int lightIntensity; // lux
float cellVoltage; // volts
long wifiRSSI; // dBm
} rawData; // declare struct variable
// *******************************************************
// ***************** INSTANTIATE OBJECTS *****************
// *******************************************************
BME280I2C myBME280; // barometric pressure / temperature / humidity sensor
BH1750 myBH1750; // light intensity sensor
WiFiClient client; // WiFi connection
// *******************************************************
// ************************ SETUP ************************
// *******************************************************
void setup()
{
Serial.begin(115200); // initialize the serial port
pinMode(LED_BUILTIN, OUTPUT); // set builtin LED for output
// initialize BME280 pressure/temperature/humidity sensor
while(!Serial) {} // Wait
Wire.begin();
while(!myBME280.begin())
{
Serial.println("Nie wykryto sensora BME/BMP280!");
delay(1000);
}
switch(myBME280.chipModel())
{
case BME280::ChipModel_BME280:
Serial.println("Wykryto sensor BME280. OK.");
break;
case BME280::ChipModel_BMP280:
Serial.println("Wykryto sensor BMP280. Odczyt wilgotności niemożliwy.");
break;
default:
Serial.println("Sensor NIEZNANY! Błąd!");
blinkLED(4); // notify the user
}
// initialize BH1750 light sensor
// the BH1750 library does not have a test for presence
myBH1750.begin();
logonToRouter(); // logon to local Wi-Fi
// load all sensor data into rawData struct
rawData = readSensors();
// send data to local serial port, ThingSpeak & APRS-IS
printToSerialPort(rawData); // display data to local serial monitor
postToRPi(rawData); // send data to RPi
// all done, now go to sleep
blinkLED(3); // notify the user
enterSleep(SLEEP_INTERVAL);
} //setup()
// *******************************************************
// ************************ LOOP *************************
// *******************************************************
void loop()
{
// there is nothing to do here
// everything is done in setup()
} // loop()
// *******************************************************
// ***************** LOGON TO YOUR Wi-Fi *****************
// *******************************************************
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);
blinkLED(5); // notify the user
enterSleep(60); // retry after 1 minute
} // if > 15
// otherwise if < 15 blink LED and wait 500ms before checking connection
blinkLED(1); // blink LED on each attempt to connect
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()
// *******************************************************
// ******** READ SENSORS INTO A SENSORDATA STRUCT ********
// *******************************************************
sensorData readSensors()
{
sensorData tempData = {0}; // initialize temporary variable to hold readings
// a fudgeFactor corrects for voltage divider component variation
// as measured by user in teh calbration step
float fudgeFactor = dmmVoltage / adcVoltage;
// BME280 pressure unit - every function expects pressure to be hPa
// BME280 temperature unit - every function expects temperature in Celsius
int samples = 3; // number of samples to take for average
// declare and initialize temporary variables
float t = 0; // temperature C
float h = 0; // humidity %
float sp = 0; // station pressure mb or hPa
float slp = 0; // sea level pressure
unsigned int li = 0; // light intensity lux
float cv = 0; // cell voltage
long ssi = 0; // signal strength
// read sensors multiple times and take average
for (int i = 0; i < samples; i++)
{
// BME280 data read: (float pressure, float temperature, float humidity, tempUnit, presUnit)
myBME280.read(sp, t, h, BME280::TempUnit_Celsius, BME280::PresUnit_hPa);
// calculate the Sea Level Pressure from the station pressure
slp = calculateSeaLevelPressure(t, sp, STATION_ELEV);
// read light level in lux
li = myBH1750.readLightLevel();
// read analog voltage from the Analog to Digital Converter
// on D1 Mini this is 0 - 1023 for voltages 0 to 3.2V
// the D1M-WX1 has an external resistor to extend the range to 5.0 Volts
cv = 5.0 * analogRead(A0) * fudgeFactor / 1023.0;
// read the Wi-Fi signal strength
ssi = WiFi.RSSI();
// accumulate the values of each sensor
tempData.temperature += t;
tempData.humidity += h;
tempData.stationPressure += sp;
tempData.seaLevelPressure += slp;
tempData.lightIntensity += li;
tempData.cellVoltage += cv;
tempData.wifiRSSI += ssi;
delay(50); // provide some delay to let sensors settle
} // for()
// divide the accumulated values by the number of samples
// to get an average
tempData.temperature /= (float)samples;
tempData.humidity /= (float)samples;
tempData.stationPressure /= (float)samples;
tempData.seaLevelPressure /= (float)samples;
tempData.lightIntensity /= (long)samples;
tempData.cellVoltage /= (float)samples;
tempData.wifiRSSI /= (int)samples;
return tempData;
} // readSensors()
// *******************************************************
// ********** PRINT RAW DATA TO THE SERIAL PORT **********
// *******************************************************
void printToSerialPort(sensorData dataRaw)
{
// '\t' is the C++ escape sequence for tab
// header line
Serial.println("\t°C\t%\thPa\tSLP hPa\tLux\tV");
// data line
Serial.print("Dane\t");
Serial.print(dataRaw.temperature, 2);
Serial.print("\t");
Serial.print(dataRaw.humidity, 2);
Serial.print("\t");
Serial.print(dataRaw.stationPressure, 2);
Serial.print("\t");
Serial.print(dataRaw.seaLevelPressure, 2);
Serial.print("\t");
Serial.print(dataRaw.lightIntensity);
Serial.print("\t");
Serial.println(dataRaw.cellVoltage, 2);
Serial.println("----------------------------------------------------");
} // printToSerialPort()
// *******************************************************
// ****************** SEND DATA TO RPi *******************
// *******************************************************
void postToRPi(sensorData data)
{
// assemble and post the data
if ( client.connect(IOT_SERVER, IOT_SERVER_PORT) == true )
{
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("t=");
client.print( data.temperature );
client.print("&&");
client.print("h=");
client.print( data.humidity );
client.print("&&");
client.print("ap=");
client.print( data.stationPressure );
client.print("&&");
client.print("rp=");
client.print( data.seaLevelPressure );
client.print("&&");
client.print("li=");
client.print( data.lightIntensity );
client.print("&&");
client.print("ps=");
client.print( data.cellVoltage );
client.print("&&");
client.print("sl=");
client.print( data.wifiRSSI );
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.");
} // postToRPi()
// *******************************************************
// calculate relative sea level pressure from absolute station pressure in hPa
// temperature in °C, elevation in m
// http://www.sandhurstweather.org.uk/barometric.pdf
// http://keisan.casio.com/exec/system/1224575267
// *******************************************************
float calculateSeaLevelPressure(float celsius, float stationPressure, float elevation)
{
float slP = stationPressure / pow(2.718281828, -(elevation / ((273.15 + celsius) * 29.263)));
return slP;
} // calculateSeaLevelPressure()
// *******************************************************
// ******************** enterSleep ***********************
// *******************************************************
void enterSleep(long sleep)
{
// sleep is in seconds
Serial.print("Wejście w tryb głębokiego snu na: ");
Serial.print(sleep);
Serial.println(" sekund.");
delay(200); // delay to let things settle
// WAKE_RF_DEFAULT wakes the ESP8266 with WiFi enabled
ESP.deepSleep(sleep * 1000000L, WAKE_RF_DEFAULT);
} // enterSleep()
// *******************************************************
// ******************** Blink LED ************************
// *******************************************************
// this flashes the onboard LED to indicate various internal messages
void blinkLED(int flashes)
{
// set LED_BUILTIN pin to Output mode in setup()
for (int i = 0; i < flashes; i++)
{
digitalWrite(LED_BUILTIN, LOW); // Turn the LED *ON*
delay(20); // short flash for low energy consumption
digitalWrite(LED_BUILTIN, HIGH); // Turn the LED *OFF*
delay(250); // time between flashes
}
} // blinkLED()
// *******************************************************
// *********************** END ***************************
// *******************************************************
Stacji Pogody nigdy za mało :) Czekam na cześć 2.
Dziękuję bardzo za zainteresowanie moim artykułem. Druga część powinna pojawić się w okolicach czwartku lub piątku (17.05 lub 18.05).
Bardzo inspirujące rozwiązania. Tylko czy czujnik BME280 zamknięty w obudowie nie przekłamuje temperatury i wilgotności? U mnie przy takim rozwiazaniu, w obudowie kondensuje się wilgoć i dosyć długo tam pozostaje. Wskazania pokazują 100% wilgotności, co na wolnym powietrzu nie jest prawdą.
Tak, BME już zaczyna szwankować (pomiar wilgotności) właśnie ze względu na obudowę.
Ooo, witam Zieloną Górę :D Z której części miasta jesteś? Po zdjęciach ciężko poznać ;)
Cześć, ul. Strumykowa. Czyli po przeciwległej stronie miasta od Ciebie :)
Ps. Po południu podeślę e-mail. Mam pewien pomysł – propozycję w związku z drugą częścią artykułu.
‘ciśnienie atmosferyczne bezwzględne (absolutne),
wyliczone ciśnienie atmosferyczne względne (relatywne),’
O co w tym chodzi?
Mam czujnik ciśnienia i mi pokazuje 958.61 hPa. Na pewno nie jest to poprawna wartość dla mojego miejsca zamieszkania. Jak to przeliczyć, żeby było tak jak ma być?
Ciśnienie powietrza zmienia się w przybliżeniu wykładniczo wraz z wysokością nad poziomem morza. Im wyższa wysokość tym ciśnienie jest mniejsze.
Ciśnienie bezwzględne (ang. absolute) – to ciśnienie, które występuje danego dnia w danej miejscowości w której znajduje się stacja pogody (barometr).
Ciśnienie względne (ang. relative) – to ciśnienie zredukowane do poziomu morza, czyli jest ciśnieniem, jakie wystąpiłoby danego dnia w danej miejscowości, gdyby znajdowała się ona na poziomie morza.
Jak wyliczyć ciśnienie względne: http://www.sandhurstweather.org.uk/barometric.pdf
Kalkulator: http://keisan.casio.com/exec/system/1224575267
Do porównywania wartości ciśnienia na różnych obszarach geograficznych musimy wykluczyć wpływ wysokości położenia stacji, stąd właśnie pokazujemy ciśnienie względne (relatywne).
Mam nadzieję, że wyjaśniłem Ci ten temat.
dzięki
Ciekawy projekt. Masz stałe czy zmienne IP?
Dziękuję. Mam zmienne IP (UPC). W drugiej części opiszę, jak sobie z tym poradzić.
Fajny projekt.
Chcę zrobić identyczny z tym że chce zastosować 2 szt. ogniw li-ion.
Gdzie kupiłeś panel solarny?
Pozdrawiam
Panel solarny kupiony tu.
Ps. A w jakim celu chcesz zastosować 2 szt. ogniw Li-Ion?
Liczę na to że 2 x większa pojemność powinna na dłużej starczyć.
Moim zdaniem nie ma takiej potrzeby. Ja zastosowałem akumulator 18650 o pojemności 3400 mAh (link). Stacja pracuje bez żadnych problemów.
Bardzo fajnie to wyszło :) Z linku widze ze to ogniwo nie ma wbudowanych zabezpieczen przed przeladowaniem/rozladowaniem… Czy jest to w tym wypadku zrobione w module TP4056? Gdzie zakupiłes ten modul?
To jest ten moduł. Jest to wersja z zabezpieczeniem.
Ps. Jest on trochę nie wygodny, gdyż wyprowadzenia nie są w rastrze 2.54 mm.
Pingback: Solarna stacja meteo z wykorzystaniem Wemos D1 mini Pro oraz Raspberry Pi 3 B+ – część 2 » Elektronika, ESP8266, Projekty bezprzewodowe, Raspberry Pi » poradnik zrób to sam na Majsterkowo.pl
Druga część tupie nóżkami w poczekalni :)
Takich ludzi Polsce potrzeba, powodzenia w projekcie.
Pingback: Solarna stacja meteo z wykorzystaniem Wemos D1 mini Pro oraz Raspberry Pi 3 B+ – część 3 » Elektronika, ESP8266, Projekty bezprzewodowe, Raspberry Pi » poradnik zrób to sam na Majsterkowo.pl
Pingback: Instrukcja wykonania amatorskiej stacji meteo – Robert Błaszczak
Można dać mniejszą kartę pamięci np. 16 GB class 10, albo nawet 8 GB, czy wystarczy?
Trzeba zakupić cały ten https://nettigo.pl/products/starter-kit-dla-raspberry-pi-3 zestaw?
Oczywiście że można 16 GB, 8 GB to trochę mało. Musisz jednak pamiętać, że dane pomiarowe w bazie danych z okresu 1,5 miesiąca to ok. 1.2 GB, a system swoje też zajmuje.
Oczywiście nie musisz kupować całego zestawu. Wystarczy “malinka” i ewentualnie zasilacz (polecam zakup oryginalnego zasilacza ze względu na spore wymagania odnośnie prądu i napięcia). Jeśli chcesz jakąś obudowę, to wybór jest spory – nie musi to być oryginalna.
Ok, czyli jaką kartę pamięci polecasz?
A te kabelki w tym zestawie? Jakbyś mógł napisać co trzeba kupić, nie za bardzo jestem w temacie?
P.S. Kiedy będzie 2 część i ile ich będzie?
Chodzi mi tutaj bardziej o to czy do projektu “Wystarczy „malinka” i ewentualnie zasilacz (polecam zakup oryginalnego zasilacza ze względu na spore wymagania odnośnie prądu i napięcia).” Ale podejrzewam że jakieś kable też są potrzebne i właśnie nie wiem jakie. Jakbyś mógł podesłać listę zakupów, które robiłeś to tego projektu.
P.S. Z tą 2 częścią pytanie nieaktualne, zauważyłem że są już 3 części.
P.S. 2 W ogóle to świetny projekt.
Ten projekt można podzielić na dwa osobne podprojekty – Raspberry Pi jako serwer WEB / bazodanowy na potrzeby zbierania danych ze stacji i ich prezentowania na stronie WWW. To nie koniecznie musi być Raspberry. Dane ze stacji meteo można również wrzucać do bazy danych na dowolnym hostingu. Tak więc w moim projekcie “malinka” robi tylko i wyłącznie jako serwer i nie są do niej podłączone żadne czujniki ze stacji pogodowej.
Drugi podprojekt, to sama stacja meteo łącząca się przez Wi-Fi do serwera bazy danych – w moim projekcie do Raspberry, ale nic nie stoi na przeszkodzie, aby baza MySQL i serwer WWW był na zewnętrznym hostingu..
Chodzi mi o listę zakupów (co potrzebuję do połączenia płytki (nie RPi) i te inne produkty
Ale taka lista jest w powyższej części opisu. Oczywiście nie jest to lista precyzyjna. Ja nie mam wiedzy, gdzie i w jaki sposób będziesz chciał zamontować stację pogodową, jaką obudowę zastosujesz, itp., itd.
Odnośnie samej elektroniki, to jest przecież schemat ideowy, z którego jasno wynika jakie elementy zostały użyte i w jaki sposób zostały ze sobą połączone.
hej,
widzialem nie tak dawno projekt takiej stacji tez na Wemos, ale panel sloneczny byl znacznie mniejszy i wystarczalo pradu.
https://www.banggood.com/5_5V-0_66W-120mA-Mini-Solar-Panel-Photovoltaic-Panel-p-1009875.html?rmmds=search&cur_warehouse=CN
Napisales, ze stacja robi pomiary co 6 minut. A nie daloby sie ograniczyc zuzycia pradu, tak aby stacja budzila sie co 6 minut, robila co ma zrobic i przechodzila w stan uspienia?
Cześć,
Nie umiem Ci odpowiedzieć, czy ze znacznie mniejszego panelu starczy prądu w warunkach zimowych. Należy mieć na uwadze krótkie, zachmurzone dni oraz niską temperaturę. Wolałem więc przewymiarować panel. Prawdziwym poligonem doświadczalnym będzie więc pierwsza dla stacji zima. Oczywiście stacja jest zaprogramowana w ten sposób, aby między pomiarami przechodziła w stan uśpienia.
Witam super opisany projekt.
Sprawdzałeś może jaki zasięg ma wifi takiego Wemos D1 ?
Dziękuję.
Ja mam na balkonie w odległości od routera ok. 9 m ale przez 3 nośnie ściany. Poziom sygnału radiowego Wi-Fi jest rejestrowany i dostępny na stronie stacji: https://malinowemeteo.zgora.pl
Mieszkam w bloku, gdzie nagromadzenie sieci Wi-Fi na częstotliwości 2.4 GHz jest ogromne. W okolicy stacji smartfon pokazuje mi ok 32 SSID.
Dziękuję za odpowiedź. Przebrnąłem przez tworzenie bazy. teraz esp i powinno działać .
Z rysunku widać jak podpiąć, w kodzie esp nie ma deklaracji pinów podpiętych do sensorów .
bawiłam się wcześniej z Arduino tego nie rozumiem ?
pozdrawiam
Witam!
Świetnie opisany projekt!
Realizuję własną stację meteo postępując wg tych instrukcji.
Mam pytanie: w jaki sposób dokonać kalibracji zasilania, wspomnianej przy deklaracji zmiennych dmmVoltage i adcVoltage? Pytam o dokładny pomiar. W tej chwili mój układ zwraca wartość około 0.11 niższą niż przy pomiarze miernikiem bezpośrednio z ogniwa.
Czy tam nie ma błędu w liście elementów?
Szukając w necie tego TP4056 pokazuje mi inny moduł- tylko do ładowania a nie ładowania i zasilania urządzeń…
Obadaj to zasilacz buforowy
Lub zastosuj Lolin (Wemos) D1 mini Pro v.2. Ta płytka ma na sobie wszystkie niezbędne elementy do budowy stacji i nie ma potrzeby wykonywania płytki drukowanej. Tu ją opisałem: https://www.blaszczak.pl/wemos-electronics-lolin-d1-mini-pro-v2-0-0/
Dzień dobry, jak stacja poradziła sobie w zimie? Planuję budowę czujnika poziomu szamba opartego o https://botland.com.pl/pl/ultradzwiekowe-czujniki-odleglosci/2743-ultradzwiekowy-czujnik-odleglosci-us-015-2-400cm.html i zastanawiam się jak radzi sobie akumulator Li-Ion na mrozie. Druga sprawa to TP4056 ma maksymalne napięcie wejściowe 5,5V, a panel daje 6V. Coś tu jest nie tak czy mi się wydaje?
Zacznę od końca :)
Super, dzięki wielkie za szybką odpowiedź. Zabieram się w takim razie do konstrukcji
Cześć, możesz więcej napisać jak obrobić takie plexi? Jak wyciąć otwór i czym kleić? Jak uszczelnić? Byłbym wdzięczny za wskazówki.
Cześć,
Temat zleciłem firmie, która zajmuje się produkcją reklam, szyldów, itp z plexi. Ja tylko dorobiłem mocowanie z kawałka płaskownika i wkleiłem na silikon panel solarny.
Bardzo przepraszam za trywialne zapewne pytanie ale jakie zadanie spełnia zworka przy module Wemos?
Kiedy należy ją zdjąć?
Z góry dziękuję i proszę o wyrozumiałość.
Zworkę zdejmujemy w momencie np. ponownego programowania Wemosa. Zdjęcie zworki wyłącza usypianie układu.
Bardzo dziękuję za odpowiedź.
Obecnie wytwarzanie prądu za pomocą energii słonecznej jest częstym wyborem, ponieważ generuje to spore oszczędności i jest ekologiczne. Można właśnie wykorzystywać to do różnego rodzaju projektów, mniejszych lub większych.
Pingback: Systemy fotowoltaiczne, systemy solarne, panele słoneczne | Город Принт | Типография Новосибирск
Pingback: Systemy fotowoltaiczne, systemy solarne, panele słoneczne - Floordeals
Cześć.
Nie mogę znaleźć odpowiedzi na pytanie, do czego służy rezystor 200k wlutowany miedzy “+” , a pin A0. Czasem widzę go też między “GND” a “A0”. Czy mogę prosić o pomoc i wyjaśnienie, co to daje ?
Z góry dziękuję
Źle spojrzałem – na innych obrazkach rezystor 200k jest także wpięty między “+”, a “A0”. Jednakże wciąż nie mogę znaleźć wyjaśnienia, do czego służy.
Mogę prosić o odpowiedź ?
Rezystor 200k tworzy dzielnik napięcia (z wbudowanym w Wemos dzielnikiem). Wejście analogowe A0 służy do pomiaru napięcia zasilania stacji.
Przy próbie kompilacji pierwszego kodu mam komunikat :
‘SLEEP_INTERVAL’ was not declared in this scope
Jak to naprawić ?
Pozdrawiam.