Estoy realizando mi TFG sobre control de calidad ambiental con una placa nodemcu v3 y varios sensores, además de un RTC DS3231 y un adaptador de tarjetas micro SD.
Tengo varias dudas. En primer lugar me gustaría saber la forma para que el programa no se quede atascado en el comando Blynk.begin(auth,ssid,pass); si no se conecta al WiFi ya que la idea es que si hay problemas de conexión, el programa siga avanzando y almacene los datos en la tarjeta microSD. He probado con el fragmento de las líneas 338 - 344 pero no lo soluciona.
Relacionado con la app Blynk tampoco sé por qué no se actualizan los valores de los sensores en mi proyecto, se quedan todo el rato a 0.
Y por último, comentar que tengo el RTC DS3231, el sensor de luminosidad TSL2591 y el de radiación gamma GDK101, conectados con comunicación I2C. Cuando pruebo por separado el TSL2591 y el GDK101, funcionan correctamente dando unos valores razonables pero al probar el código al completo, el TSL2591 marca -1 lux y el GDK101 56 Bq/m^3. Buscando información, en principio no debería de pasar esto porque tienen direcciones I2C diferentes. Agradecería mucho si alguien saber algo sobre esto.
Muchas gracias de antemano a toda la comunidad.
#define BLYNK_PRINT Serial // Definición para poder mostrar los datos en la
// aplicación Blynk
// Se incluyen las librerías necesarias:
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <Wire.h>
// Para los sensores DHT22
#include <DHT.h>
// Para el sensor de luminosidad
#include <Adafruit_Sensor.h>
#include "Adafruit_TSL2591.h"
// Para el adaptador de tarjetas microSD
#include <SPI.h>
#include <SdFat.h> // Mejor que la SD.h en cuanto al consumo
// Para el uso del reloj RTC DS3231
#include "RTClib.h"
/*
* Direcciones I2C por defecto:
* RTC DS3231 :: 0x57 0x68
* GDK 101 :: 0x18
* TSL2591 :: 0x29
*/
char auth[] = "YDWKmVVIEWo_hJE42ZPg7O7niEVtN5hm"; // Identificador del proyecto en Blynk
//char ssid[] = "MOVISTAR_B802"; // Introducir el nombre de la red Wifi
//char pass[] = "n22WaAZWmN822fma83QU"; // Contraseña de la red Wifi
//char ssid[] = "FTE-DDE8";
//char pass[] = "twsw72Y3";
//char ssid[] = "Redmi";
//char pass[] = "12345678";
SimpleTimer timer; // Sincroniza el reloj Arduino con Blynk
#define DHTPIN 2 // Se define el pin de conexión del sensor DHT22, D4 (GPIO2)
#define DHTTYPE DHT22 // Se elige el sensor de la familia DHT que se utiliza
DHT dht(DHTPIN, DHTTYPE); // Configuración de la librería DHT
float Ha, Ta; // Declaración de variables del DHT22
#define CO2 A0 // Se define la entrada analógica del sensor MH-Z14A
float ppm; // Declaración de variable del MH-Z14A
int addr = 0x18; // Dirección por defecto del sensor GDK101
byte buffer[2] = {0,0}; // Se establece el bus que se utilizará en la interfaz I2C
int day,hour,minute,sec = 0; // Se establecen los datos secundarios del sensor, para
int status = 0; // visualizar por monitor serie
float value2; // Declaración variable del GDK101
Adafruit_TSL2591 tsl = Adafruit_TSL2591(2591); // Pasar un número para el identificador del sensor
uint16_t lux; // Declaración variable del TSL2591
#define DHTPIN_g 0 // Pin de conexión del sensor DHT22 para la Tg, D3 (GPIO0)
DHT dht_g(DHTPIN_g, DHTTYPE); // Configuración de la librería DHT para la Tg
float Hg, Tg, To; // Declaración de variables del sensor de temperatura operativa
const int chipSelect = 4; // Lector SD
SdFat SD; // Necesario para sustituir SdFat.h por SD.h
File logFile;
char fileName[] = "datalog.csv"; // Nombre del fichero de salida donde se guardarán las medidas
#define LOGFILE "datalog.csv"
RTC_DS3231 rtc;
char buf[] = "DD/MM/YY \t hh:mm:ss";
// Función para leer los sensores
void SendSensor(){
Ha = dht.readHumidity(); // Se obtiene la humedad relativa del ambiente
Ta = dht.readTemperature(); // Se obtiene la temperatura seca del ambiente
if (isnan(Ha) || isnan(Ta)){
Serial.println("Failed to read from DHT sensor!");
return;
}
Serial.println();
Serial.print(F("Humedad relativa: \t\t"));
Serial.print(Ha);
Serial.println(F(" %"));
Serial.print(F("Temperatura seca: \t\t"));
Serial.print(Ta);
Serial.println(F(" °C"));
float v = analogRead(CO2)*3.3/1023.0; // Convertir a voltaje la entrada A0
ppm = int((v-0.4)*3125.0); // Calcular concentración CO2 en ppm con la tensión de entrada A0
Serial.println();
Serial.print(F("Concentración CO2: \t\t"));
Serial.print(ppm);
Serial.println(F(" ppm"));
// Asociar los datos con las variables virtuales de Blynk
Blynk.virtualWrite(V1, Ha); // V1 es para humedad relativa ambiental
Blynk.virtualWrite(V2, Ta); // V2 es para la temperatura seca ambiental
Blynk.virtualWrite(V3, ppm); // V3 es para ppm de CO2
// Llamada a la función Gamma_Mod_Read_Value() del sensor GDK101
Serial.println();
Gamma_Mod_Read_Value();
// Función para leer a la vez el espectro completo e infrarrojo y convertirlo a lux
Serial.println();
advancedRead();
delay(500);
// Mediciones del sensor DHT22 colocado en la semiesfera negra mate para la Tg
Hg = dht_g.readHumidity(); // Se obtiene la humedad relativa en el interior de la semiesfera (de globo)
Tg = dht_g.readTemperature(); // Se obtiene la temperatura de globo
if (isnan(Hg) || isnan(Tg)){
Serial.println("Failed to read from balloon sensor!");
return;
}
// Velocidad del aire
float Va = 0.2;
// Cálculo de la temperatura radiante media a partir de la temperatura de globo (Tg) y del aire (t)
double Trm = pow((pow((Tg+273.0),4.0) + 2.5*pow(10.0,8.0)*pow(Va,0.6)*(Tg-Ta)),0.25) - 273.0;
// Cálculo de la temperatura operativa
To = (Ta+float(Trm))/2;
Serial.println();
Serial.print(F("Temperatura operativa: \t\t"));
Serial.print(To);
Serial.println(F(" ºC"));
Serial.println();
Serial.println(F("*****************************************************************"));
Blynk.virtualWrite(V6, To); // V6 es para la temperatura operativa
}
void Gamma_Mod_Read_Value(){
Gamma_Mod_Read(0xB0); // Función para mostrar el funcionamiento del sensor GDK101
Gamma_Mod_Read(0xB1); // Función para mostrar el tiempo de medición
Gamma_Mod_Read(0xB2); // Función para realizar la media de las mediciones cada 10 minutos
Gamma_Mod_Read(0xB3); // Función para la lectura de los valores medidos cada minuto
}
// Función para leer los datos del sensor GDK101
void Gamma_Mod_Read(int cmd){
Wire.beginTransmission(addr); // Se comienza a escribir la secuencia
Wire.write(cmd);
Wire.endTransmission(); // Fin de escritura de la secuencia
delay(10);
Wire.requestFrom(addr, 2); // Comienza a leer la secuencia
// Bucle para leer cada uno de los datos
byte i = 0;
while(Wire.available())
{
buffer[i] = Wire.read();
i++;
}
// Fin de lectura de la secuencia
// Llamada a función para mostrar los resultados del GDK101
Print_Result(cmd);
}
void Print_Result(int cmd){
float value = 0.0f; // Declaracion de las variables que se utilizarán
switch(cmd){ // Selección del comando
case 0xA0: // Si se resetea (comando 0xA0) muestra por
// el monitor serie si se ha realizado o no
Serial.print(F("Reset Response\t\t\t"));
if(buffer[0] == 1) Serial.println("Reset Success.");
else Serial.println("Reset Fail(Status - Ready).");
break;
case 0xB0:
Serial.print(F("GDK101 Status\t\t\t")); // Muestra el funcionamiento del sensor
switch(buffer[0]){ // En función de los bits del buffer
case 0: Serial.println("Ready"); break;
case 1: Serial.println("10min Waiting"); break;
case 2: Serial.println("Normal"); break;
}
status = buffer[0];
Serial.print(F("VIB Status\t\t\t"));
switch(buffer[1]){ // Se comprueba si se ha detectado vibración
case 0: Serial.println("OFF"); break; // Si el bit 1 del buffer es 0, se mostrará PFF
case 1: Serial.println("ON"); break; // Si el bit 0 del buffer es 1, se mostrará ON
}
break;
case 0xB1:
if(status > 0){
sec++; // Aumenta cada segundo
Cal_Measuring_Time(); // Llamada a la función de medición de tiempo
}
break;
case 0xB2:
Serial.print(F("Measuring Value(10min avg)\t"));
value = buffer[0] + (float)buffer[1]/100; // Valor medio cada 10 minutos
Serial.print(value); Serial.println(" uSv/hr");
break;
case 0xB3: // Comando para obtener los valores medidos
Serial.print(F("Measuring Value(lmin avg)\t"));
value = buffer[0] + (float)buffer[1]/100; // Valor medido en uSv / hr
Serial.print(value); Serial.println(F(" uSv/hr"));
Serial.print(F("Concentración de Radón: \t"));
float value2;
if (value == 0){
value2 = 0;
}
else{
// Definición de las variables que se utilizan para el cálculo de CRn
float lambda = 0.0000021;
float densidad = 1600;
float H = 3.5;
float E = 0.25;
float tv = 0.0027;
float D = 0.000002;
value2 = (value*114-0.451)*E*densidad*sqrt(lambda*D)/(0.1887*H*(lambda+tv)); // Obtención del valor en Bq / m^3
}
Serial.print(value2);
Serial.println(F(" Bq/m^3"));
Blynk.virtualWrite(V4,value2); // V4 es el valor medido en Bq / m^3
break;
case 0xB4:
Serial.print(F("FW Version\t\t\t"));
Serial.print(F("V")); Serial.print(buffer[0]);
Serial.print(F(".")); Serial.println(buffer[1]);
break;
}
}
void Cal_Measuring_Time(){
if(sec == 60) { sec = 0; minute++; }
if(minute == 60) { minute = 0; hour++; }
if(hour == 24) { hour = 0; day++; }
Serial.print(F("Measuring Time\t\t\t"));
Serial.print(day); Serial.print(F("d "));
if(hour < 10) Serial.print(F("0"));
Serial.print(hour); Serial.print(F(":"));
if(minute < 10) Serial.print(F("0"));
Serial.print(minute); Serial.print(F(":"));
if(sec < 10) Serial.print(F("0"));
Serial.println(sec);
}
// Función para leer los datos del sensor TSL2591
void advancedRead(void)
{
// More advanced data read example. Read 32 bits with top 16 bits IR, bottom 16 bits full spectrum
// That way you can do whatever math and comparisons you want!
uint32_t lum = tsl.getFullLuminosity();
uint16_t ir, full;
ir = lum >> 16;
full = lum & 0xFFFF;
lux = tsl.calculateLux(full, ir);
Serial.print(F("Luminosidad: \t"));
Serial.print(F("IR: ")); Serial.print(ir); Serial.print(F(" "));
Serial.print(F("Full: ")); Serial.print(full); Serial.print(F(" "));
Serial.print(F("Visible: ")); Serial.print(full - ir); Serial.print(F(" "));
Serial.print(F("Lux: ")); Serial.println(tsl.calculateLux(full, ir));
Blynk.virtualWrite(V5, lux); // V5 es para la luminosidad
}
// Función para los detales del sensor TSL2591
void displaySensorDetails(void)
{
sensor_t sensor;
tsl.getSensor(&sensor);
Serial.println(F("--------------------------------"));
Serial.print (F("Sensor: ")); Serial.println(sensor.name);
Serial.print (F("Driver Ver: ")); Serial.println(sensor.version);
Serial.print (F("Unique ID: ")); Serial.println(sensor.sensor_id);
Serial.print (F("Max Value: ")); Serial.print(sensor.max_value); Serial.println(F(" lux"));
Serial.print (F("Min Value: ")); Serial.print(sensor.min_value); Serial.println(F(" lux"));
Serial.print (F("Resolution: ")); Serial.print(sensor.resolution, 4); Serial.println(F(" lux"));
Serial.println(F("--------------------------------"));
delay(500);
}
// Función para configurar el sensor TSL2591
void configureSensor(void)
{
tsl.setGain(TSL2591_GAIN_MED); // 25x gain
tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS); // integration time
/* Display the gain and integration time for reference sake */
Serial.print (F("Gain: "));
tsl2591Gain_t gain = tsl.getGain();
switch(gain)
{
case TSL2591_GAIN_LOW:
Serial.println(F("1x (Low)"));
break;
case TSL2591_GAIN_MED:
Serial.println(F("25x (Medium)"));
break;
case TSL2591_GAIN_HIGH:
Serial.println(F("428x (High)"));
break;
case TSL2591_GAIN_MAX:
Serial.println(F("9876x (Max)"));
break;
}
Serial.print (F("Timing: "));
Serial.print((tsl.getTiming() + 1) * 100, DEC);
Serial.println(F(" ms"));
Serial.print(F("==============================================="));
Serial.println(F(""));
}
void setup() {
Serial.begin(9600); // Velocidad de transmisión para monitor serie
Wire.begin(4,5); // Se establecen las entradas para la interfaz I2C
// GPIO4 (D2) = SDA y GPIO5 (D1) = SCL
rtc.begin(); // Inicializa el reloj RTC DS3231
// Cabecera de presentación
Serial.println(F(" "));
Serial.println(F("Datalogger. Elena Martínez 2021"));
Serial.println(F("Sensores de radón y temperatura operativa en nodos para controlar la calidad ambiental en entornos interiores"));
Serial.println(F("TFG. Ingenieria Electrónica, Robótica y Mecatrónica"));
Serial.println(F("Placa Node MCU"));
DateTime date = rtc.now();
Serial.println(date.toString(buf));
Serial.println(" ");
Blynk.begin(auth,ssid,pass); // Configuración para conectarse a BLynk
// Prueba
uint8_t i = 0;
while (WiFi.status() != WL_CONNECTED && i++ < 20) delay(500);
if(i == 21){
Serial1.print("Could not connect to"); Serial1.println(ssid);
delay(500);
}
// Para el adaptador SD
pinMode(chipSelect,OUTPUT);
// Para lo sensores DHT22
dht.begin();
dht_g.begin();
// Llamada a la función leer sensores cada 5 minutos (1000 s * 60 s * 5 min = 300.000)
timer.setInterval(60000L, SendSensor);
// Parte del sensor GDK101
Serial.println(F("==============================================="));
Serial.println(F("Gamma Sensor Sensing Start"));
// Read Firmware version
Gamma_Mod_Read(0xB4);
// Llamada a la función leer datos del sensor GDK101 para resetearlo
Gamma_Mod_Read(0xA0);
Serial.println(F("==============================================="));
// Parte del sensor TSL2591
Serial.println(F("Starting Adafruit TSL2591!"));
if (tsl.begin()) {
Serial.println(F("Found a TSL2591 sensor"));
}
else {
Serial.println(F("No sensor found ... check your wiring?"));
while (1);
}
// Display some basic information on this sensor
displaySensorDetails();
// Configure the sensor
configureSensor();
// Parte del adaptador SD
if (!SD.begin(chipSelect,SPI_HALF_SPEED)) { // Chequeo
Serial.println(F("Fallo en el adaptador de tarjetas microSD"));
}
else {
logFile = SD.open(fileName, FILE_WRITE); // Abrir fichero
if (logFile) { // Si está disponible, escribir en él
logFile.println(F("Fecha\tHora\tHR(%)\tTa(ºC)\tCO2(ppm)\tRn(Bq/m^3)\tLuminosidad(lux)\tHg(%)\tTg(ºC)\tTo(ºC)"));
logFile.close(); // Cerrar el fichero
Serial.println(F("... Cabecera de datos grabados"));
Serial.println(F("... Dispositivos inicializados"));
Serial.println(F("==============================================="));
// Cabecera de inicio de datos
// Serial.println(F("Fecha\t\tHora\tHR(%)\tTa(ºC)\tCO2(ppm)\tRn(Bq/m^3)\tLuminosidad(lux)\tHg(%)\tTg(ºC)\tTo(ºC)"));
}
else {
Serial.println(F("Fallo en el adaptador de tarjetas microSD"));
}
}
}
void loop() {
Blynk.run(); // Inicializar Blynk
timer.run(); // Inicializar SimpleTimer
delay(90000L); // Dar tiempo a mostrar los datos en el monitor serie
// Datos para la tarjeta SD y para mostrar en el monitor serie
// Cabecera de inicio de datos
Serial.println(F("Fecha\t\tHora\tHR(%)\tTa(ºC)\tCO2(ppm)\tRn(Bq/m^3)\tLuminosidad(lux)\tHg(%)\tTg(ºC)\tTo(ºC)"));
DateTime now = rtc.now();
String nowStr = now.toString(buf);
String dataString = nowStr +"\t"+String(Ha)+"\t"+String(Ta)+"\t"+String(ppm)+"\t"+String(value2)+"\t"+String(lux)+"\t"+String(Hg)+"\t"+String(Tg)+"\t"+String(To)+"\t";
Serial.println(dataString);
if (!SD.begin(chipSelect,SPI_HALF_SPEED)){ // Chequeo
Serial.println(F("Fallo en el adaptador de tarjetas microSD"));
}
else {
logFile = SD.open(fileName, FILE_WRITE); // Abrir fichero
if (logFile) { // Si está disponible, escribir en él
logFile.println(dataString); // Enviar al fichero un string con los datos
logFile.close(); // Cerrar el fichero
Serial.println(dataString); // Enviar al monitor serie el mismo string
}
else {
Serial.println(F("Fallo en el adaptador de tarjetas microSD"));
}
}
}