- Цена: $1.38
Сегодня будем делать простейшую интернет-метеостанцию. Никаких выносных датчиков, никаких дисплеев — просто измеряет температуру-влажность-давление и отправляет в интернет, на сайты narodmon.ru и www.wunderground.com. Как говорится, добавь и свою могучую кучку в гору мировой бигдаты!!!
Нам понадобится платка Wemos D1 Mini (клоник за 2$), платка с датчиком BME280 (2.55$), какой-нибудь корпус и wi-fi-роутер с интернетом.
Схема простая до безобразия.
Главное выполнить всё на пайке, а не на штырьках, а то от дребезга контактов у BME280 иногда сносит крышу и он начинает выдавать полный бред, пока ему питание не передернешь.
Вот так выглядит функционально полностью рабочий полуфабрикат метеостанции.
Далее нужно упаковать это в корпус и повесить за окно. Если есть возможность разместить метеостанцию на западной стороне — никаких проблем. Но у меня такой возможности нет и примерно 4ч в день метеостанция будет на солнце, поэтому я пока еще в раздумьях — как-бы снизить погрешность без громоздкой жалюзийной защиты датчика? Если знаете хорошие варианты — предлагайте в каментах. Питается вся конструкция от USB-порта wifi-роутера — это позволяет, при необходимости, удаленно перезагрузить метеостанцию вместе с роутером.
Теперь по софту — будем использовать Arduino IDE. Там всё стандартно — устанавливаем, в меню выбираем: Инструменты->Плата->Lolin (Wemos) D1 R2 & mini, при необходимости, устанавливаем библиотеку Adafruit BME280 library и оукей. C китайскими платами на BME280 есть один нюанс — они могут быть с разным адресом. Обычно это 0x76 или 0x77, хотя бывают и более экзотичные варианты. В общем, если датчик вдруг не работает, то ищем на компе файл Adafruit_BME280.h, в нем ищем строчку #define BME280_ADDRESS (0x76) и в ней меняем 0x76 на 0x77, ну или наоборот.
#include <Adafruit_BME280.h>
#include <PubSubClient.h>
Adafruit_BME280 bme; // I2C
// Replace with your network details
const char* ssid = «WIFINET»;
const char* password = «пароль_вайфай»;
float h, t, tF, p, pb, pin, dp, dpF;
char temperatureString[6];
char temperatureFString[6];
char dpString[6];
char dpFString[6];
char humidityString[6];
char pressureString[7];
char pressureInString[6];
#define SRV «narodmon.ru»
#define MAC «хх:dd:c2: хх:a3: хх» //МАК-адрес метеостанции
#define PASS «ххххх» // пароль для mqtt постинга на narodmon
#define USERNAME «yyyyyyyyy» // логин на narodmon
#define TOPIC «yyyyyyyy/BME280/»
#define SRV2 «weatherstation.wunderground.com»
#define StationID «ZZZZZZZ» // ID станции на сайте wunderground.com
#define PASS2 «xxxxx» // Key станции на сайте wunderground.com
//MQTT Narodmon
char server[] = SRV;
char authMethod[] = USERNAME;
char token[] = PASS;
char clientId[] = MAC;
char conntopic[] = TOPIC «status»;
//HTTP GET WUnderground
char server2[] = SRV2;
char authMethod2[] = StationID;
char token2[] = PASS2;
char webpage[] = «GET /weatherstation/updateweatherstation.php?»;
WiFiClient nmClient;
WiFiClient wuClient;
PubSubClient clientMQ(server, 1883, nmClient);
// only runs once on boot
void setup() {
// Initializing serial port for debugging purposes
Serial.begin(115200);
delay(10);
// Connecting to WiFi network
Serial.println();
Serial.print(«Connecting to „);
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(“.»);
}
Serial.println("");
Serial.println(«WiFi connected»);
// Printing the ESP IP address
Serial.println(WiFi.localIP());
Serial.println(F(«BME280 start»));
if (!bme.begin()) {
Serial.println(«Could not find a valid BME280 sensor, check wiring!»);
while (1);
}
}
// runs over and over again
void loop() {
getWeather();
doPublishNM(«humidity», String(h, 2));
doPublishNM(«temperature», String(t, 2));
doPublishNM(«pressure», String(p, 2));
doPublishWU(temperatureFString, pressureInString, humidityString, dpFString);
delay(600000);
}
void getWeather() {
h = 0;
float valh;
for (int i=0; i<30; i++) {
delay (random(50,500));
valh = bme.readHumidity();
Serial.print(valh);
Serial.print("n");
h = h+valh;
}
h=h/30;
Serial.print("n");
t = 0;
float valt;
for (int i=0; i<30; i++) {
delay (random(50,500));
valt = bme.readTemperature();
Serial.print(valt);
Serial.print("n");
t = t+valt;
}
t=t/30;
tF=(t*1.8)+32;
Serial.print(tF);
p = 0;
float valp;
for (int i=0; i<30; i++) {
delay (random(50,500));
valp = bme.readPressure();
Serial.print(valp);
Serial.print("n");
p = p+valp;
}
p=p/30/100.0F;
dp = t-((1-(h/100))/0.05);
dpF = (dp*1.8)+32;
pb = p/pow(2.718281828, -0.029*9.81*150/(8.31*(t+273.15))); // приводим абсолютное давление к уровню Балтийского моря (высота 150м)
pin = p*0.0296133971008484;
dtostrf(t, 5, 2, temperatureString);
dtostrf(tF, 4, 2, temperatureFString);
dtostrf(h, 5, 2, humidityString);
dtostrf(p, 6, 2, pressureString);
dtostrf(pin, 4, 2, pressureInString);
dtostrf(dpF, 5, 2, dpFString);
}
// MQTT публикация Narodmon.ru
void doPublishNM(String id, String value) {
// если не подключен, то подключаемся. Висит пока не подключится!!!
if (!!!clientMQ.connected()) {
Serial.print(«Reconnecting client to „); Serial.println(server);
while (!!!clientMQ.connect(clientId, authMethod, token, conntopic,0,0,“online»)) {
Serial.print(".");
delay(500);
}
Serial.print(«connected with: „); Serial.print(clientId); Serial.print(authMethod); Serial.print(token);
Serial.println();
}
String topic = TOPIC;
String payload = value;
// String topic += id;
topic.concat(id);
Serial.print(“Publishing on: „); Serial.println(topic);
Serial.print(“Publishing payload: „); Serial.println(payload);
if (clientMQ.publish(topic.c_str(), (char*) payload.c_str())) {
Serial.println(“Publish ok»);
} else {
Serial.println(«Publish failed»);
}
}
// HTTP GET публикация WUnderground
void doPublishWU(String temperatureFString, String pressureInString, String humidityString, String dpFString) {
if (wuClient.connect(server2, 80)) {
Serial.print(F("… Connected to server: "));
Serial.print(server2);
char c = wuClient.read();
Serial.print(F(", Server response: "));
Serial.write©;
Serial.println(F(""));
Serial.println(F("… Sending DATA "));
Serial.println(F(""));
wuClient.print(webpage);
Serial.print(webpage);
wuClient.print(«ID=»);
Serial.print(«ID=»);
wuClient.print(authMethod2);
Serial.print(authMethod2);
wuClient.print("&PASSWORD=");
Serial.print("&PASSWORD=");
wuClient.print(token2);
Serial.print(token2);
wuClient.print("&dateutc=");
Serial.print("&dateutc=");
wuClient.print(«now»);
Serial.print(«now»);
wuClient.print("&tempf=");
Serial.print("&tempf=");
wuClient.print(temperatureFString);
Serial.print(temperatureFString);
wuClient.print("&baromin=");
Serial.print("&baromin=");
wuClient.print(pressureInString);
Serial.print(pressureInString);
wuClient.print("&humidity=");
Serial.print("&humidity=");
wuClient.print(humidityString);
Serial.print(humidityString);
wuClient.print("&dewptf=");
Serial.print("&dewptf=");
wuClient.print(dpFString);
Serial.print(dpFString);
wuClient.print("&softwaretype=Arduino%20UNO%20version1&action=updateraw");
Serial.print("&softwaretype=Arduino%20UNO%20version1&action=updateraw");
//Finishing the communication
wuClient.println("/ HTTP/1.1rnHost: host:portrnConnection: closernrn");
Serial.println(F("… Server Response:"));
while(wuClient.connected()) {
while (wuClient.available()) {
char c = wuClient.read();
Serial.print©;
}
}
wuClient.flush();
wuClient.stop();
}
else {
Serial.println(F(«Connection failed»));
char c = wuClient.read();
Serial.write©;
wuClient.flush();
wuClient.stop();}
}
Да, вот такой скетч — кривенький, но простой и рабочий. Раз в 10 минут делает по 30 отсчетов, усредняет и отправляет показания по протоколу MQTT на narodmon.ru и по протоколу HTTP на wunderground.com. Хотел еще добавить постинг на openweathermap.org, но ниасилил — в конце концов я же не настоящий сварщик и погроммист из меня примерно такой же, как балерина.
Само собой, предварительно нужно зарегистрироваться на обоих сайтах, а также получить на narodmon.ru пароль для MQTT (в справке по API) и зарегистрировать погодную станцию на wunderground.com, чтобы получить ID и Key.
На этом, пожалуй, всё. Думаю, тут всё просто и каждый, при желании, сможет модифицировать этот полуфабрикат под себя — добавить датчик освещенности или дождя, например. Можно подумать над deep sleep и автономным питанием на 4хАА или 5хАА и т.п…