Tecnología Informática      Técnico en Instalaciones de Telecomunicaciones About

 Estación Meteorológica con NodeMCU

 

conexion BME 280Seguimos avanzando en nuestro proyecto usando en NodeMcu como servidor, pero ahora vamos a añadir un dispositivo BME280 que por medio de un puerto serie, vamos a obtener los datos de Temperatura, presión y Humedad

Como siempre, tenemos que instalar nuestras librerías para poder comunicarnos con éste componente.

La idea sobre la que se desarrolla este proyecto es el control de instalaciones en el campo ( granjas, invernaderos, depósitos de agua, etc ) con comunicaciones basadas en GPRS, 3G o 4G.

Nos encontraremos con el problema que en telefonía móvil, aunque tengamos acceso a los puertos del router para abrir el puerto 80, el proveedor de Internet no lo va a permitir, dado que nuestra IP es compartida.

1º BME 280

Lo primero es conocer un poco sobre el módulo sensor BME280, usado con  el ESP8266 para leer la presión, la temperatura, la humedad y la altitud (dado que la presión cambia con la altitud, también puede estimar la altitud.). El sensor BME280 utiliza el protocolo de comunicación I2C o SPI para intercambiar datos con la placa NodeMcu.

Se explicará cómo  conectar el sensor al ESP8266, instalar las bibliotecas necesarias y escribir un sencillo esquema que muestre las lecturas del sensor. En una segunda parte, grabaremos los datos en una base de datos MySql.

Vamos a usar la comunicación I2C con el módulo de sensores BME280. Para ello, conecte el sensor a los pines SDA y SCL del ESP8266, como se muestra en el siguiente diagrama.

nodemcu a bme280

2º Instalación de la biblioteca BME280


Para obtener lecturas del módulo de sensores de la BME280 es necesario utilizar la biblioteca Adafruit_BME280. Siga los siguientes pasos para instalar la biblioteca en su IDE de Arduino:

1º Abra su IDE de Arduino y vaya a Programa > Incluir librería > Gestionar librerías. El Administrador de Librerías debería abrirse.

Busque "adafruit bme280 " en el cuadro de búsqueda e instale la biblioteca. Dejamos una imagen del proceso, aunque en nuestro caso, ya la tenemos instalada en el IDE
adafruit bme280


3º Instalando la librería Adafruit_Sensor
 

Para usar la biblioteca BME280, también necesita instalar la biblioteca Adafruit_Sensor. Siga los siguientes pasos para instalar la biblioteca en su IDE de Arduino:

Vaya a Programa > Incluir librería > Gestionar librerías  y escriba "Adafruit Unified Sensor" en el cuadro de búsqueda. Desplácese hacia abajo para encontrar la biblioteca e instalarla. También la tenemos instalada.

Adafruit Unified Sensor

Una vez que hemos instalado las librerías, y con los archivos html y Css de la práctica anterior (Servidor web con NodeMCU ) , vamos a cargar nuestro programa con el nuevo código de la estación meteorológica.

/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com
*/

// Importamos las librerías
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

Adafruit_BME280 bme; // I2C

// Sustituye los datos de tu red WIFI ( el nombre y la contraseña )
const char* ssid = "profesores_electronica";
const char* password = "**************";



// Configuramos el LED GPIO
const int ledPin = 2;
// Guardamos LED state
String ledState;

// Creamos un objeto usando AsyncWebServer sobre el puerto 80
AsyncWebServer server(80);

// leemos la temperatura y la mostramos
String getTemperature() {
float temperature = bme.readTemperature();
Serial.println(temperature);
return String(temperature);
}
// leemos la humedad y la mostramos
String getHumidity() {
float humidity = bme.readHumidity();
Serial.println(humidity);
return String(humidity);
}
// leemos la presion y la mostramos
String getPressure() {
float pressure = bme.readPressure()/ 100.0F;
Serial.println(pressure);
return String(pressure);
}

// Sustituimos el marcador con el estado del LED
String processor(const String& var){
Serial.println(var);
if(var == "STATE"){
if(digitalRead(ledPin)){
ledState = "ON";
}
else{
ledState = "OFF";
}
Serial.print(ledState);
return ledState;
}
else if (var == "TEMPERATURE"){
return getTemperature();
}
else if (var == "HUMIDITY"){
return getHumidity();
}
else if (var == "PRESSURE"){
return getPressure();
}
}

void setup(){
// Configuramos la velocidad del puerto
Serial.begin(9600);
// El ledPin lo ponemos como salida
pinMode(ledPin, OUTPUT);

// Iniciamos el sensor
if (!bme.begin(0x76)) {
Serial.println("NO podemos encontrar el sensor BME280. Comprueba el cableado!");
while (1);
}

// Iniciamos SPIFFS
if(!SPIFFS.begin()){
Serial.println("Se ha producido un error al montar SPIFFS");
return;
}

// Conectamos a la red WIFI
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
// esperamos un segundo para nuevo intento
delay(1000);
Serial.println("Conectando a la red WiFi..");
}
// Imprimimos la IP del NodeMCU
Serial.println(WiFi.localIP());
// Ruta para cargar el archivo index.html
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Ruta para cargar el archivostyle.css
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/style.css", "text/css");
});
//Ahora ponemos 5 bloques donde se hace las consultas al servidor sobre el estado del LED y las tres variables atmosféricas
// Ruta para poner el GPIO a HIGH
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, HIGH);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Ruta para poner el GPIO a LOW
server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, LOW);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", getTemperature().c_str());
});
server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", getHumidity().c_str());
});
server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", getPressure().c_str());
});
// Inicio del servidor
server.begin();
}
void loop(){
}
 

 Cuidado. Podemos encontrarnos con el error:


Could not find a valid BME280 sensor, check wiring!
 

En este caso, es conveniente que miremos la posible Solución que dejamos al final de la página

 

2º Parte.  Estación meteorológica con aviso por email


El siguiente paso es mandar un email desde nuestro nodeMCU. La idea es avisar si un determinado valor excede de uno fijado, por ejemplo, nos avise si la temperatura excede de 25 grados
Si tenemos en cuenta el trabajo desarrollado por -->https://randomnerdtutorials.com/esp32-esp8266-send-email-notification/<--  tenemos que la respuesta del servidor no es adecuada.

Vamos a explicarlo por pasos. Dejamos el archivo de prueba ( donde hemos quitado el BME 280 para simplificar ). Bajar archivo AQUI

 

El archivo que va a recibir los datos del nodeMCU debe estar alojado donde dice

const char* serverName = "tudominio.es/email-notification.php";

Tenemos que sustituir tudominio.es por el dominio nuestro y crear un archivo email-notification.php donde vamos a poner nuestro código php

Bajamos de internet el edito Lopeedit para modificar este archivo 

Dejamos el archivo php  AQUI  email-notification.php

<?php
// Donde mandamos el correo
$email_address = "escribe tu email";
//  Valor API Key  que debe ser el mimos para asegurarnos que la solicitud se manda desde nuestro NOdeMcu
$api_key_value = "tPmAT5Ab3j7F9";
// Ponemos las variables a cero
$api_key = $value1 = $value2 = $value3 = "";
// Se ejecuta si la petición es por el sistema POST
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$api_key = test_input($_POST["api_key"]);
// si la clave mandada coincide con la de este archivo, mandamos el correo
if($api_key == $api_key_value) {
$value1 = test_input($_POST["value1"]);
$value2 = test_input($_POST["value2"]);
$value3 = test_input($_POST["value3"]);
// Mensaje del correo
$email_msg = "Temperature: " . $value1 . "ºC\nHumidity: " . $value2 . "%\nPressure: " . $value3 . "hPa";

// Quitamos por ahora la condición de no mandar el email si la temperatura es inferior a 30 grados
/*if($value1 < 30.0){
echo "No mandamos el correo porque la temperatura es inferior a 30º";
exit;
}*/

// Mandamos el correo
mail($email_address, "[NEW] ESP BME280 Readings", $email_msg);

echo "Correo enviado";
}
else {
echo "La clave API no es correcta";
}
}
else {
echo "No hay datos POST recibidos.";
}

function test_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
?>

El primer problema que vamos a encontrar es el siguiente:


Conectamos con la url 
 http.begin(serverName);
    
    // Ahora especificamos el contenido de cabecera 
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    
    // Se prepara los datos para el envio de  HTTP POST 
                            String httpRequestData = "api_key=" + apiKeyValue + "";
// solo vamos a mandar una variable , la api key , para probar el código.
//Vemos en consola los datos que mandamos
    Serial.print("httpRequestData: ");
    Serial.println(httpRequestData);
    
  Enviamos los datos a la url con la información que nos interesa ( por POST )
    // Send HTTP POST request
    int httpResponseCode = http.POST(httpRequestData);
     
//Vemos  la respuesta que nos dá nuestro servidor por medio de
if (httpResponseCode>0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
// en estas dos líneas es donde vamos a obtener la respuesta del servidor 
      String payload = http.getString(); 
Serial.println(payload); //Print request response payload 
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    // Free resources
    http.end();
  }

Terminado todo esto nos encontramos con un mensaje  largo donde podemos ver el siguiente texto
This site requires Javascript to work, please enable Javascript in your browser or use a browser with Javascript support
 

La razón es la siguiente

Los hosting, y sobre todos los compartidos, y más aún los gratuitos, tienen  un sistema de seguridad que ayuda a proteger los sitios web contra los robots.  Este mensaje es el propio del sistema de seguridad, porque no se ha detectado que la consulta se haga desde un navegador, sino un dispositivo IOT como nuestro NodeMCU

Si hacemos lo mismo en otro servidor sin estas limitaciones, tenemos la respuesta ok 200 con el envío de email.

conexion por post


Por tanto, es muy importante que antes de seguir con esta práctica, comprobemos que nuestro hosting va a aceptar una petición POST desde un IOT

La solución (pendiente de confirmar ) está en el tipo gestor de archivos del servidor, Nginx o Apache.
Donde he realizado las pruebas es un servidor del la empresa OVH, tipo VPS donde  se utiliza plesk para configurar el servidor. 
Para uno de los dominios, tengo 
Apache & nginx Settings for midominio.ovh
 Proxy mode:  Nginx proxies requests to Apache. Turn off to stop using Apache.

 

3º Parte.  Guardamos datos en MySql de nuestra  Estación meteorológica

Lo primero es crear nuestra base de datos y obtener las credenciales para poder entrar por MysqlAdmin.

Luego hacemos una consulta Sql con el siguiente texto

CREATE TABLE SensorData (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
sensor VARCHAR(30) NOT NULL,
localizacion VARCHAR(30) NOT NULL,
temperatura VARCHAR(10),
humedad VARCHAR(10),
presion VARCHAR(10),
lectura TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
Proyecto final. Consideraciones

En el la carpeta comprimida que nos podemos bajar AQUÍ   tenemos 5 archivos, que paso a comentar

1º Dentro de la carpeta estación, tenemos otra con data, donde tenemos el archivo CSS  para dar los estilos a nuestra página web ( la que vemos al entrar en la IP del servidor NodeMcu ) y otro archivo que presenta los datos en tiempo real ( 10 segundos de refresco ) de los datos de nuestra estación

2º En esa carpeta “estacion” tenemos nuestro archivo .ino donde tenemos que cambiar tres datos. La SSID, la clave SSID y la URL de la página que recoge los datos POST mandado por arduino. 

3º El archivo email-notification.php , que tenemos que poner en nuestro dominio ( CUIDADO CON EL SERVIDOR QUE ELEGIMOS.  VER ARRIBA ). Este archivo recoge los datos POST y los guarda en la base de datos Mysql. Si la temperatura excede de una crítica, se manda un email de aviso

4º Archivo mostramos-datos.php, que se encarga de mostrar la tabla con los datos meteorológicos. En el mismo se ha incorporado una sentencia if de forma que para una determinada hora, se borren todos los datos. Se puede cambiar para que ello se haga una vez al mes
Además, cada línea se  muestra en un color diferente según la temperatura. Si es normal, en azul, si sube algo , en amarillo, si excede la tª crítica, en rojo.

Solución a problemas del sensor BME 280

Problema de reconocimiento del BME 280
A veces nos encontramos con el siguiente mensaje de error
Could not find a valid BME280 sensor, check wiring!

A pesar de tener todas las librerías instaladas y las conexiones correctas, el BME 280 no funciona
Solución
1º Debemos de encontrar por qué dirección se comunica el BME con nuestro microcontrolador. para ello, vamos a hacer un test de direcciones para comprobar si es la 0x76 o la 0x77
Cargar y ejecutar el siguiente programa  para Comprobar la dirección del BME 280 Aquí
2º Si tu dirección es 0x77 no tienes que cambiar nada
Si su dirección es diferente y no tenemos una librería actualizada, donde se consideren las dos direcciones, proceder de la siguiente manera
a) Localizar el archivo de la biblioteca Adafruit_BME280.h - normalmente se guarda en
C:\Users\Usuario\Documents\Arduino\libraries\Adafruit_BME280_Library
b) Encontrar #definir BME280_ADDRESS (0x76) y cambiar a su dirección escaneada.
Ahora comprobamos si funciona

Si todavía tienes el mismo problema, abre el archivo Adafruit_BME280.cpp -
que está junto al archivo anterior
Encontrar
_sensorID = read8(BME280_REGISTER_CHIPID);
if (_sensorID != 0x60)
return false;

Y sustituir por

_sensorID = read8(BME280_REGISTER_CHIPID);
if (_sensorID != 0x60)
// return false;

En esta última prueba, la biblioteca de adafruit comprueba el CHIPID y su chipid es diferente de lo que vende adafruit.

Diseño PCB Principios básicos de antenas

Ciclo de Grado Medio Instalaciones de Telecomunicaciones. Ies Mare Nostrum. Málaga