Помогите нашему проекту

Еще одна погодная станция на ESP8266 Избранное

Еще одна погодная станция на ESP8266 Еще одна погодная станция на ESP8266

Я решил мониторить погодные показатели у себя дома. У меня уже есть богатый опыт сборки устройств на arduino и esp8266 и на esp мне понравилось больше (из-за наличия wi-fi).

для полноценной картины погодных условий нам необходимо знать температуру внутри и снаружи, давление воздуха, влажность и качество воздуха (количество CO и CO2). Показания буду считывать домашним сервером на Linux при помощи самописного скрипта и выводить через MRTG в виде графика.

Собственно детали были заказаны на Aliexpress: MP180 (температура и давление), MQ135 (качество воздуха), DTH11 (температура и влажность), DTH22 (уличная температура и влажность), 662k (стабилизаторы на 3.3 вольта). Доставка была быстрой, и уже через две недели все было готово к сборке.
К сожалению, на момент написания статьи DTH22 еще не пришел, но я оставил для него выведенный внешний разъем.

Я не буду выкладывать схему, так как нет принципиальной разницы к каким GPIO  что цеплять (за исключением i2c шины, она необходима для датчика давления). Сборку начнем с впаивания esp8266 в плату с контактами, где кстати, сразу отпаяем перемычку и впаяем стабилизатор на 3.3 вольта. Как раз для этого были куплены десяток 662k стабилизаторов. Это нам очень упростит дальнейшее подключение, так как все датчики работают либо от 3.3 до 5 в, либо строго от 3.3. Получается питать всю схему можем любым напряжением и не париться, что что-то сгорит.

Перемычка на ESP8266

Для удобства, я вывел питание со стабилизатора на макетку, откуда потом развел на плюс всех устройтв.

Стабилизатор 662k на ESP8266  Стабилизатор 662k на ESP8266

Далее расположил все на макетной плате, чтоб было поудобнее, все впаял, развел провода сзади (плюс на плюс, GND на минус, остальное на GPIO, сопротивления для светодиодов и аналогового входа, об этом чуь позже), вывел несколько светодиодов, чтобы понимать что происходит, поставил PC спикер для понта (пищит при включении и превышении показаний). 

Погодная станция на ESP8266 

Кстати у ESP8266 есть один аналоговый вход, но он считывает напряжение до 1 вольта. У датчика загазованности MQ135, есть два выхода - цифровой и аналоговый. Цифровой срабатывает при превышении показаний, поэтому для мониторинга он бесполезен, а второй выгод аналоговый. Вот его то мы и подключим к ESP. Но для корректной работы нам нужно собрать простой делитель напряжения на двух сопротивлениях.

mq135 + esp8266

После подключения и написания скетча на Adruino оказалось, что в режиме i2c могут работать не все контакты (как я ранее упоминал). В итоге мне так и не удалось заставить работать по i2c никакие GPIO кроме дефолтных (GPIO4, GPIO5). Конечно не принципиально, но пришлось разок перепаять схему.
Экранчик я решил не подключать, так как показания всегда можно посмотреть с мобильника или компьютера. Кроме того показания мониторятся с сервера (у меня дома свой сервер) и выводятся в MRTG.

ESP8266 mrtg

Собственно сам скетч будет в самом низу, но сразу скажу, пока еще не финальная версия, так как DTH22 не пришел. 

Для мониторинга температуры выбрал SimpleDHT.h, потому что с ним не глючит, а со стандартным постоянно вместо температуры "nan". Кроме того оказалось, что по одному датчику температуры и по другому (один в DHT11, другой MP180) температура отличается на 3 градуса, при этом на настольном градуснике температура равна средненму от них обоих. Я не стал заморачиваться и считаю, что верная температура, та которая средняя. Для датчика давления использовал SFE_BMP180.h. Загазованность можно просто считывать с аналогового выгода, но зависимость показаний не линейная, поэтому сложно анализировать. Для того, чтобы упростить скетч я использовал MQ135.h. Главное, после того как установите MQ135, необходимо дать ему 24 часа поработать, чтобы прогреться. Затем нужно считать нулевые значения при помощи команды:

float rzero = gasSensor.getRZero();

Как получили нулевые значения, их нужно вписать в файл MQ135.h в переменную RZERO.

Далее, давайте рассмотрим веб-страничку, которую генерит погодная станция.

 

Если посмотреть код страницы, в самом низу можно увидеть скрытый код. Между скрытыми элементами <!start data> и <!stop data> находятся данные для сервера.
Так проще обрабатывать.
На сервере (Linux) по крону запускается MRTG, которая стартует скрипт:

curl -s http://192.168.1.63 | sed -n '/<!start data><!/,/><!stop data>/p'|grep -v "data" > /tmp/ws.tmp
grep nvAvrIntTemp /tmp/ws.tmp -A1|tail -1
grep nvHmD1 /tmp/ws.tmp -A1|tail -1

Ну и по аналогии получаем остальные данные. То есть вместо nvAvrIntTemp может быть, например, nvMq1, тогда мы получим не среднюю температуру, а качество воздуха.  Настройку MRTG я не буду расписывать, она совершенно стандартна, но если будет интересно, выложу, хотя там ничего замороченного нет.

Weather Stantion ESP8266

Скетч:

#include <Wire.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <SFE_BMP180.h>
#include <SimpleDHT.h>
#include "MQ135.h"


MQ135 gasSensor = MQ135(A0);
SFE_BMP180 pressure;
#define ALTITUDE 145.0
int pinDHT11 = 14;
int pinDHT112 = 13;
extern "C" {
#include "user_interface.h"
}

int led=16;
int beep=12;
int dht1=1;
int dht2=1;
int TempD1 = 0;
int HmD1 = 0;
int TempD2 = 0;
int HmD2 = 0;
float Mq1 = 0;

// Имя хоста
const char* host = "esp8266-ws";
// Вместо звездочек свои параметры WiFi сети
const char* ssid = "name";
const char* password = "password";

int MD=0;
int MQA=1024;
float TA1=1024;
float TA2=1024;
float HA1=1024;
float HA2=1024;
int PA=1024;



ESP8266WebServer server(80);

void setup() {
// Последовательный порт для отладки
Serial.begin(115200);

Serial.printf("\n\nFree memory %d\n",ESP.getFreeHeap());
wifi_set_sleep_type(NONE_SLEEP_T);
Serial.printf("\n\nOff sleep mode\n");

// Подлючение к WiFi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if(WiFi.waitForConnectResult() == WL_CONNECTED){
// Запуск WEB-сервера
// MDNS.begin(host);
server.on ( "/", HTTP_handleRoot );
server.onNotFound ( HTTP_handleRoot );
server.begin();
Serial.printf( "HTTP server ok! Open http://%s.local in your browser\n", host);
Serial.print("WiFi connected, IP address: "); Serial.println(WiFi.localIP());


pressure.begin();

pinMode(beep, OUTPUT);
pinMode(led, OUTPUT);

digitalWrite(led,LOW);
digitalWrite(led,HIGH);
tone(beep,100,200);
delay(150);
digitalWrite(led,LOW);
tone(beep,500,200);
delay(150);
tone(beep,1000,200);
digitalWrite(led,HIGH);
delay(150);
digitalWrite(led,LOW);
noTone(beep);
digitalWrite(beep,LOW);
//float rzero = gasSensor.getRZero();
//Serial.print("Zero ");
//Serial.println(rzero);

}
else {
Serial.printf("WiFi started is fail");
}

}


void loop() {
server.handleClient();
delay(50);
}


/*
* Оработчик главной страницы сервера
*/
void HTTP_handleRoot(void) {
char status;
double T,P,p0,a;
if( server.hasArg("MD") ){
MD=server.arg("MD").toInt();
}

if( server.hasArg("MQA") ){
MQA=server.arg("MQA").toInt();
}
if( server.hasArg("HA1") ){
HA1=server.arg("HA1").toInt();
}
if( server.hasArg("HA2") ){
HA2=server.arg("HA2").toInt();
}
if( server.hasArg("TA1") ){
TA1=server.arg("TA1").toInt();
}
if( server.hasArg("TA2") ){
TA2=server.arg("TA2").toInt();
}
if( server.hasArg("PA") ){
PA=server.arg("PA").toInt();
}
byte bTempD1;
byte bHmD1;
byte bTempD2;
byte bHmD2;
if (simple_dht11_read(pinDHT11, &bTempD1, &bHmD1, NULL)) {
dht1=0;
}
else {
dht1=1;
TempD1=(int)bTempD1;
HmD1=(int)bHmD1;
}
if (simple_dht11_read(pinDHT112, &bTempD1, &bHmD1, NULL)){
dht2=0;
}
else {
dht2=0;
TempD1=(int)bTempD2;
HmD1=(int)bHmD2;
}
Mq1= gasSensor.getPPM();
digitalWrite(led,HIGH);
delay(150);
digitalWrite(led,LOW);
delay(150);
digitalWrite(led,HIGH);
delay(150);
digitalWrite(led,LOW);
if (isnan(HmD1) || isnan(TempD1)) {
Serial.println("Failed to read from DHT sensor 1 (int)!");
}
if (isnan(HmD2) || isnan(TempD2)) {
Serial.println("Failed to read from DHT sensor 2 (ext)!");
}


status = pressure.startTemperature();
if (status != 0)
{
// Wait for the measurement to complete:
delay(status);

// Retrieve the completed temperature measurement:
// Note that the measurement is stored in the variable T.
// Function returns 1 if successful, 0 if failure.

status = pressure.getTemperature(T);
if (status != 0)
{
// Print out the measurement:
Serial.print("temperature: ");
Serial.print(T,2);
Serial.print(" deg C, ");
Serial.print((9.0/5.0)*T+32.0,2);
Serial.println(" deg F");

// Start a pressure measurement:
// The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
// If request is successful, the number of ms to wait is returned.
// If request is unsuccessful, 0 is returned.

status = pressure.startPressure(3);
if (status != 0)
{
// Wait for the measurement to complete:
delay(status);

// Retrieve the completed pressure measurement:
// Note that the measurement is stored in the variable P.
// Note also that the function requires the previous temperature measurement (T).
// (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
// Function returns 1 if successful, 0 if failure.

status = pressure.getPressure(P,T);
if (status != 0)
{
// Print out the measurement:
Serial.print("absolute pressure: ");
Serial.print(P,2);
Serial.print(" mb, ");
Serial.print(P*0.0295333727,2);
Serial.println(" inHg");

// The pressure sensor returns abolute pressure, which varies with altitude.
// To remove the effects of altitude, use the sealevel function and your current altitude.
// This number is commonly used in weather reports.
// Parameters: P = absolute pressure in mb, ALTITUDE = current altitude in m.
// Result: p0 = sea-level compensated pressure in mb

p0 = pressure.sealevel(P,ALTITUDE); // we're at 1655 meters (Boulder, CO)
Serial.print("relative (sea-level) pressure: ");
Serial.print(p0,2);
Serial.print(" mb, ");
Serial.print(p0*0.0295333727,2);
Serial.println(" inHg");

// On the other hand, if you want to determine your altitude from the pressure reading,
// use the altitude function along with a baseline pressure (sea-level or other).
// Parameters: P = absolute pressure in mb, p0 = baseline pressure in mb.
// Result: a = altitude in m.

a = pressure.altitude(P,p0);
Serial.print("computed altitude: ");
Serial.print(a,0);
Serial.print(" meters, ");
Serial.print(a*3.28084,0);
Serial.println(" feet");
}
else Serial.println("error retrieving pressure measurement\n");
}
else Serial.println("error starting pressure measurement\n");
}
else Serial.println("error retrieving temperature measurement\n");
}
else Serial.println("error starting temperature measurement\n");

String out = "";

out =
"<html><head>\
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\
<title>MainFrm SH (id: WS1)</title>\
<style>\
.h4 {margin: 0px 0px;}\
body {color: #555; font-family: helvetica neue, helvetica, arial; font-size: 15px; line-height: 2em; text-align:center; }\
a { color: #473213; text-decoration: none; padding: 2px;}\
a:hover { text-decoration: none; }\
</style>";
out+=
"</head>\
<body>\
<p><b><font size=\"4\" color=\"#FF0000\">M</font>ainFrm SH(<font color=\"#008000\">id:</font> WS1)</b></p>\
<hr size=\"1px\" width=\"75%\">";

out+="\
<b>\nTempD1: \n</b>\n";
if (dht1==1){
out+=TempD1;
} else {
out+="no sensor";
}
out+="\
\n<br><b>\
\nHmD1: \n</b>\n";
if (dht1==1){
out+=HmD1;
} else {
out+="no sensor";
}
out+="\n<br><b>\
\nTempD2: \n</b>\n";
if (dht2==1){
out+=TempD2;
} else {
out+="no sensor";
}
out+="\
\n<br><b>\
\nHmD2: \n</b>\n";
if (dht2==1){
out+=HmD2;
} else {
out+="no sensor";
}

out+="\n<br><b>\
\nMq1: \n</b>\n";
out+=Mq1;
out+="\n<br><b>\
\nTempD3: \n</b>\n";
out+=T;
out+="\n<br><b>\
\nAvrIntTemp: \n</b>\n";
out+=(TempD1+T)/2;
out+="\n<br><b>\
\nPrD3: \n</b>\n";
out+=P*0.750062;
out+="\n<br><b>\
\nPrSeaLD3: \n</b>\n";
out+=p0*0.750062;
out+="\n<br><b>\
\nAltD3: \n</b>\n";
out+=a;
out+="\n<br><hr size=\"1px\" width=\"75%\">";
out+="Manual alarm MORE: \
<a href=\"/?MD=1&MQA=50\">Test MQ1 (50)</a> \
<a href=\"/?MD=2&TA1=10\">Test Av. Temp (10)</a> \
<a href=\"/?MD=3&TA2=10\">Test Ext. Temp (10)</a> \
<a href=\"/?MD=5&HA1=50\">Test Hm1 (50)</a> \
<a href=\"/?MD=6&HA2=50\">Test Hm2 (50)</a> \
<a href=\"/?MD=4&PA=500\">Test Pr. (500)</a> \
<br> Manual alarm LESS: \
<a href=\"/?MD=7&MQA=50\">Test MQ1 (50)</a> \
<a href=\"/?MD=8&TA1=10\">Test Av. Temp (10)</a> \
<a href=\"/?MD=9&TA2=10\">Test Ext. Temp (10)</a> \
<a href=\"/?MD=11&HA1=50\">Test Hm1 (50)</a> \
<a href=\"/?MD=12&HA2=50\">Test Hm2 (50)</a> \
<a href=\"/?MD=10&PA=1000\">Test Pr. (1000)</a> \
<br></body>\
</html>";


//---

out+="\n\n<!start data><!\
\nnvTempD1: \n";
if (dht1==1){
out+=TempD1;
} else {
out+="no sensor";
}
out+="\
\nnvHmD1: \n";
if (dht1==1){
out+=HmD1;
} else {
out+="no sensor";
}
out+="\
\nnvTempD2: \n";
if (dht2==1){
out+=TempD2;
} else {
out+="no sensor";
}
out+="\
\nnvHmD2: \n";
if (dht2==1){
out+=HmD2;
} else {
out+="no sensor";
}
out+="\
\nnvMq1: \n";
out+=Mq1;
out+="\
\nnvTempD3: \n";
out+=T;
out+="\
\nnvAvrIntTemp: \n";
out+=(TempD1+T)/2;
out+="\
\nnvPrD3: \n";
out+=P*0.750062;
out+="\
\nnvPrSeaLD3: \n";
out+=p0*0.750062;
out+="\
\nnvAltD3: \n";
out+=a;
out+="\n\
><!stop data>";

//---

 

server.send ( 200, "text/html", out );


if( MD==1 ){
Serial.print("MQ1 Sensor:");
Serial.println(Mq1);
if (MD==1 & Mq1>=MQA)
{
Alarm();
}
MD=0;
}
if( MD==2 ){
Serial.print("Av. temp:");
int tmp=(TempD1+T)/2;
Serial.println(tmp);
if (MD==2 & tmp>=TA1)
{
Alarm();
}
MD=0;
}
if( MD==3 ){
Serial.print("Temp Ext:");
Serial.println(TempD2);
if (MD==2 & TempD2>=TA2)
{
Alarm();
}
MD=0;
}
if( MD==4 ){
Serial.print("Pr Sensor:");
Serial.println(P);
if (MD==4 & P>=PA)
{
Alarm();
}
MD=0;
}
if( MD==5 ){
Serial.print("HmD1 Sensor:");
Serial.println(HmD1);
if (MD==5 & HmD1>=HA1)
{
Alarm();
}
MD=0;
}
if( MD==6 ){
Serial.print("HmD2 Sensor:");
Serial.println(HmD2);
if (MD==6 & HmD2<=HA2)
{
Alarm();
}
MD=0;
}
if( MD==7 ){
Serial.print("MQ1 Sensor:");
Serial.println(Mq1);
if (MD==7 & Mq1<=MQA)
{
Alarm();
}
MD=0;
}
if( MD==8 ){
Serial.print("Av. temp:");
int tmp=(TempD1+T)/2;
Serial.println(tmp);
if (MD==8 & tmp<=TA1)
{
Alarm();
}
MD=0;
}
if( MD==9 ){
Serial.print("Temp Ext:");
Serial.println(TempD2);
if (MD==9 & TempD2<=TA2)
{
Alarm();
}
MD=0;
}
if( MD==10 ){
Serial.print("Pr Sensor:");
Serial.println(P);
if (MD==10 & P<=PA)
{
Alarm();
}
MD=0;
}
if( MD==11 ){
Serial.print("HmD1 Sensor:");
Serial.println(HmD1);
if (MD==11 & HmD1<=HA1)
{
Alarm();
}
MD=0;
}
if( MD==12 ){
Serial.print("HmD2 Sensor:");
Serial.println(HmD2);
if (MD==12 & HmD2<=HA2)
{
Alarm();
}
MD=0;
}

}

void Alarm()
{
digitalWrite(led,LOW);
digitalWrite(led,HIGH);
tone(beep,100,200);
delay(150);
digitalWrite(led,LOW);
tone(beep,500,200);
delay(150);
tone(beep,1000,200);
digitalWrite(led,HIGH);
delay(150);
digitalWrite(led,LOW);
tone(beep,500,200);
digitalWrite(led,HIGH);
tone(beep,100,200);
delay(150);
digitalWrite(led,LOW);
tone(beep,500,200);
delay(150);
tone(beep,1000,200);
digitalWrite(led,HIGH);
delay(150);
tone(beep,500,200);
digitalWrite(led,HIGH);
tone(beep,100,200);
delay(150);
digitalWrite(led,LOW);
tone(beep,500,200);
delay(150);
tone(beep,1000,200);
digitalWrite(led,HIGH);
delay(150);
digitalWrite(led,LOW);
noTone(beep);
digitalWrite(beep,LOW);
}

 

 Всем спасибо. Продолжение следует...

808 комментарии

  • lilly cialis 5 mg
    lilly cialis 5 mg 18.01.2022 18:32 Комментировать

    acheter viagra pfizer achat viagra feminin blague de quel pays vien le viagra viagra generique ou original

  • cialis alternative
    cialis alternative 16.01.2022 20:00 Комментировать

    vente viagra acheter viagra en ligne sans ordonnance elle prend du viagra porn combien de temps le viagra fait il effet

  • precio viagra
    precio viagra 15.01.2022 00:06 Комментировать

    plaquenil scleroderma hydroxychloroquine medication plaquenil dose for rheumatoid arthritis how does plaquenil work for arthritis

  • how to buy stromectol
    how to buy stromectol 12.01.2022 14:19 Комментировать

    cialis gratis cialis senza ricetta roma cialis funziona per le donne cosa e il cialis

  • purchase viagra
    purchase viagra 11.01.2022 02:55 Комментировать

    viagra contraindicaciones hipertension receta viagra que pasa si tomas viagra caducada como funciona viagra

  • precio de cialis 5 mg
    precio de cialis 5 mg 07.01.2022 19:14 Комментировать

    cialis e alcol cialis 5mg cialis prima o dopo i pasti dove acquistare cialis italia

  • exittee
    exittee 06.01.2022 02:08 Комментировать

    Allergies. Give away the whole show your doctor if you procure ever had any singular or allergic reaction to this medicine or any other medicines. Also describe your healthiness heed proficient if you have any other types of allergies, such as to foods, dyes, preservatives, or animals. For non-prescription products, scan the epithet or combine ingredients carefully. Pediatric. Studies on this remedy would rather been done barely in adults and in children weighing 15 kilograms (kg) (33 pounds) and from, and there is no delineated bumf comparing manipulate of ivermectin in dogs in children weighing less than 15 kg with make use of in other period groups. Geriatric. Many medicines get not been planned specifically in older people. Therefore, it may not be known whether they work unequivocally the word-for-word way they do in younger adults or if they originator distinguishable side effects or problems in older people. There is no definitive report comparing use of ivermectin in the golden-agers with have recourse to in other age groups. injectable ivermectin for humans

  • admitad moneyveo
    admitad moneyveo 05.01.2022 14:33 Комментировать

    Hello my family member! I wish to say that this article is amazing, nice written and include almost all vital infos.
    I would like to see more posts like this .

  • when to take cialis
    when to take cialis 03.01.2022 21:24 Комментировать

    cialis paypal when does cialis go generic in australia maximum dose of cialis in 24 hours what does viagra do to women

  • online cialis
    online cialis 03.01.2022 05:21 Комментировать

    viagra sildenafil viagra ricetta obbligatoria viagra per durare di piГ№ dove comprare il viagra a roma

Оставить комментарий

Убедитесь, что вы вводите (*) необходимую информацию, где нужно
HTML-коды запрещены

Back to top

Помогите нашему проекту