-
Notifications
You must be signed in to change notification settings - Fork 1
/
AirQ.ino
381 lines (335 loc) · 13.9 KB
/
AirQ.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
/*
AirQ 0.1 - Medidor de la calidad del aire
Autor: David Fdez. Mtnez. - 02/04/2020
*/
// Librería que vamos a utilizar
#include <LiquidCrystal_I2C.h> // para la pantalla LCD
#include "DHT.h" // para el sensor de temperatura y humedad
#include "RTClib.h" // para el reloj de tiempo real (RTC)
#include "WiFiEsp.h" //Libreria para comunicarse facilmente con el modulo ESP01 wifi
#include "SoftwareSerial.h" // La necesita WiFiEsp
#include <Wire.h> // Para poder utilizar el bus de comunicaciones I2C (pantalla y reloj de tiempo real)
// definiciones
#define TIPO_DHT DHT11 // tipo de sensor de temperatura/humedad que vamos a usar
#define PIN_DHT11 2 // pin para el sensor de temperatura/humedad
#define PIN_LED 4 // pin para el led de alarma
#define PIN_ZUMBADOR 6 // pin para el zumbador de alarma
#define PIN_RX 11 // pin de recepción para el módulo wifi
#define PIN_MQ_DIGITAL 9 // pin digital para el sensor MQ-135 que utilizaremos para alarma
#define PIN_TX 12 // pin de transmisión para el módulo wifi
#define PIN_MQ_ANALOGICO PIN_A0 // pin analógico para el sensor MQ-135 que utilizaremos para la medición
// constantes
const String ID_DISPOSITIVO = "01"; // para identificar el dispositivo que está realizando las medidas cuando se tengan varios
const int RETARDO = 2000; // indica en milisegundos cada cuánto tiempo realizamos medición, actualizamos la pantalla...
// Configuracion wifi
char ssid[] = "PON_AQUI_TU_SSID"; // SSID (Nombre de la red WiFi)
char pass[] = "PON_AQUI_TU_PASS"; // Contraseña
int status = WL_IDLE_STATUS; // Estado del ESP. No tocar.
// Configuración de IFTTT
char host[] = "maker.ifttt.com"; // url del sitio IFTTT maker
char evento[] = "controla_calidad_aire"; // evento que vamos a utilizar para registrar las medidas en la hoja Google
char key[] = "PON_AQUI_TU_KEY"; // clave de la API de IFTTT. La podemos consultar con nuestra cuenta en IFTTT.
// Control del tiempo transcurrido para el envío de información al servicio web
long marcaTiempoAnteriorServicioWeb = 0; // me servirá para saber si tengo que enviar ya la información al servicio web o no
const long intervaloServicioWeb = 60000; // (milisegundos) controla cada cuánto tiempo vamos a enviar la información.
// Por ejemplo, 1 min (1*60*1000) = 60000
// Pantalla
LiquidCrystal_I2C lcd(0x3F, 20, 4); // creamos el objeto que modela la pantalla LCD. Los parámetro son:
// - dirección para el bus I2C (0x3F en mi caso)
// - columnas (20)
// - filas (4)
// Sensor temperatura - humedad
DHT dht(PIN_DHT11, TIPO_DHT); // creamos el objeto que modela el sensor de temperatura y humedad, asociado
// al pin correspondiente y del tipo definido.
// Reloj de tiempo real
RTC_DS3231 rtc; // creamos el objeto que modela el reloj de tiempo real para mostrar fecha/hora en pantalla
// Wifi
WiFiEspClient client; // creamos el objeto que modela el cliente wifi
SoftwareSerial esp8266(PIN_RX, PIN_TX); // y también al dispositivo ESP8266 que utilizamos para conectar por wifi
// Definimos dos caracteres personalizados para la pantalla: una cara sonriente cuando no hay alarma y una cara
// triste que se visualizará cuando haya alarma.
byte sonrisa[8] = {
0b00000,
0b00000,
0b01010,
0b00000,
0b10001,
0b01110,
0b00000,
0b00000
};
byte pena[8] = {
0b00000,
0b00000,
0b01010,
0b00000,
0b01110,
0b10001,
0b00000,
0b00000
};
void setup() // este bloque se ejecuta sólo al principio de la ejecución
{
Serial.begin(9600); // iniciamos comunicación serie para poder ver la información en el monitor serie a 9600 baudios.
esp8266.begin(9600); // iniciamos la comunicación serie para el ESP (wifi)
lcd.init(); // Inicializamos el LCD
lcd.backlight(); // Encendemos la luz de fondo del LCD
// Damos de alta los dos caracteres especiales que necesitaremos
lcd.createChar(1, sonrisa);
lcd.createChar(2, pena);
lcd.clear(); // Limpiamos la pantalla
// Escribimos en el LCD el título de la aplicación y la identificación del dispositivo
lcd.print("[ AirQ v0.1 - ID");
lcd.print(ID_DISPOSITIVO);
lcd.print(" ]");
mostrarIniciandoDisplay(); // mostramos un mensaje indicando que estamos iniciando el dispositivo
// establecemos un par de pines que van a ser de salida
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_ZUMBADOR, OUTPUT);
dht.begin(); // inicializamos el sensor de temperatura y humedad
rtc.begin(); // inicializamos el reloj de tiempo real
if (rtc.lostPower()) { // si se queda sin batería cogemos la fecha de compilación
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
iniciarWifi(); // llamamos a una función que nos inicializará el wifi
// verEstadoWifi(); // esta función nos mostraría en el monitor datos de la conexión.
// la tengo quitada para ahorrar algo de memoria, pero se puede poner para depurar errores.
borraDisplay(); // borramos la línea 2, 3 y 4 del display para empezar a ofrecer información
}
void loop() // y esto es lo que se ejecuta repetidamente
{
// recojo la medida de los sensores
int medidaAnalogica = analogRead(PIN_MQ_ANALOGICO);
bool estado = digitalRead(PIN_MQ_DIGITAL);
float temperatura = dht.readTemperature();
float humedad = dht.readHumidity();
DateTime ahora = rtc.now();
// para tener el CO2 en ppm hay que hacer algunas cuentas.
// Los parámetros están cogidos del proyecto de Ulises Gascon https://github.com/UlisesGascon
// Supondría que tenemos exactamente el mismo sensor que él.
// Por lo que todo esto habría que comprobarlo y calcularlo correctamente para el sensor concreto a usar.
// Ya sea por calibración en ambiente controlado o utilizando las tablas que proporciona el fabricante.
float tension = medidaAnalogica * (5.0 / 1023.0);
float resistencia = 1000 * ((5 - tension) / tension);
double CO2ppm = 245 * pow(resistencia / 5463, -2.26); // el dato que realmente nos interesa...
// mostramos la información obtenida por el monitor serie (en el PC si lo tenemos conectado)
mostrarInfoLog(medidaAnalogica, tension, CO2ppm, estado, (int) temperatura, (int) humedad, ahora, ID_DISPOSITIVO);
// actualizamos la información en la pantalla LCD
mostrarInfoDisplay(CO2ppm, estado, (int) temperatura, (int) humedad, ahora);
// Vemos ahora si tenemos que mostrar la alarma. Si estado es verdadero, "no hay de qué preocuparse".
// Si estado es falso, hemos superado el nivel establecido con el potenciómetro del MQ-135, por lo que
// encenderemos el LED y emitiremos un pitido en cada ciclo
if (estado) {
digitalWrite(PIN_LED, LOW);
} else {
digitalWrite(PIN_LED, HIGH);
tono(1);
}
// esperamos el retardo establecido
delay(RETARDO);
// ahora vemos si nos toca enviar información por el servicio web
unsigned long marcaTiempoActual = millis(); // obtenemos la marca de tiempo actual
if (marcaTiempoActual - marcaTiempoAnteriorServicioWeb > intervaloServicioWeb) { // comprobamos si toca
Serial.println("Registrando información"); // informamos por el monitor serie que vamos a enviar
mostrarEnviandoDisplay(); // mostramos en pantalla que se está realizando un envío
marcaTiempoAnteriorServicioWeb = marcaTiempoActual; // refrescamos la marca de tiempo
// llamamos a una función que nos hemos definido para el envío de la información obtenida en este ciclo
consumirServicio(evento, (int) CO2ppm, (int) estado, (int) temperatura, (int) humedad, ID_DISPOSITIVO);
borraDisplay(); // tras el envío, preparamos la pantalla para volver a mostrar la información
}
}
// Esta función se encarga de mostrar por el monitor serie (en el PC) los datos que se le proporcionan.
// Es bastante intuitiva.
void mostrarInfoLog(int medidaAnalogica, float tension, double CO2ppm, bool estado,
int temperatura, int humedad, DateTime tiempo, String idDispositivo) {
Serial.print("Medida analógica: ");
Serial.print(medidaAnalogica);
Serial.print(" Tension: ");
Serial.print(tension);
Serial.print(" PPM CO2: ");
Serial.print(CO2ppm);
Serial.print(" Estado: ");
if (!estado)
{
Serial.print("Alarma");
}
else
{
Serial.print("Normal");
}
Serial.print(" Temp: ");
Serial.print(temperatura);
Serial.print(" Humedad: ");
Serial.print(humedad);
Serial.print(" Hora: ");
Serial.print(tiempo.year());
Serial.print("-");
if (tiempo.month() < 10) Serial.print("0");
Serial.print(tiempo.month());
Serial.print("-");
Serial.print(tiempo.day());
Serial.print(" ");
Serial.print(tiempo.hour());
Serial.print(":");
Serial.print(tiempo.minute());
Serial.print(":");
Serial.print(tiempo.second());
Serial.print(" ID: ");
Serial.println(idDispositivo);
}
// esta función se encarga de pintar la información por el display. Es bastante intuitiva.
void mostrarInfoDisplay(double CO2ppm, bool estado, int temperatura, int humedad, DateTime ahora) {
lcd.setCursor(0, 1);
lcd.print("CO2: ");
for (int n = 5 ; n < 19; n++)
{
lcd.print(" ");
}
lcd.setCursor(5, 1);
lcd.print(CO2ppm);
lcd.print(" ppm");
lcd.setCursor(19, 1);
if (estado) {
lcd.write(1);
} else {
lcd.write(2);
}
lcd.setCursor(0, 2);
lcd.print("Temp: ");
lcd.print(temperatura);
lcd.print((char)223);
lcd.print("C");
lcd.setCursor(11, 2);
lcd.print("Hum: ");
lcd.print(humedad);
lcd.print(" %");
lcd.setCursor(3, 3);
if (ahora.day() < 10) lcd.print("0");
lcd.print(ahora.day(), DEC);
lcd.print('/');
if (ahora.month() < 10) lcd.print("0");
lcd.print(ahora.month(), DEC);
lcd.print('/');
lcd.print(ahora.year() % 1000);
lcd.print(' ');
lcd.print(ahora.hour(), DEC);
lcd.print(':');
if (ahora.minute() < 10) lcd.print("0");
lcd.print(ahora.minute(), DEC);
}
// Esta función se encarga de pintar el mensaje cuando el dispositivo se está iniciando.
void mostrarIniciandoDisplay() {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print(" Iniciando ... ");
lcd.setCursor(0, 3);
lcd.print(" ");
}
// Esta función se encarga de pintar el mensaje cuando el dispositivo está enviado información.
void mostrarEnviandoDisplay() {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print(" Enviando datos... ");
lcd.setCursor(0, 3);
lcd.print(" ");
}
// Esta función deja en blanco todo el display salvo la primera línea.
void borraDisplay() {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print(" ");
}
// esta función se encarga de iniciar el módulo ESP (wifi)
void iniciarWifi() {
WiFi.init(&esp8266); // inicio Wifi
//intentar iniciar el modulo ESP
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("Modulo no presente. Reinicie el Arduino y el ESP01 (Quite el cable que va de CH_PD a 3.3V y vuelvalo a colocar)");
while (true); // no hemos podido iniciar el wifi, nos quedamos aquí hasta que nos reinien el dispositivo
}
// Al llegar aquí hay conexión. Intenta conectar a la red wifi concreta que se ha configurado
while ( status != WL_CONNECTED) {
Serial.print("Intentando conectar a la red WiFi: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
}
}
// esta función se encarga de enviar mediante el servicio web de IFTTT los datos para que acaben reflejados
// en la hoja de Google
void consumirServicio(String evento, int valor1, int valor2, int valor3, int valor4, String valor5) {
Serial.println("Iniciando conexion..."); // Informamos por el monitor serie
if (client.connect(host, 80)) { // Intentamos la conexión
Serial.println("Conectado al servidor");
// Construimos la URL. Lo vamos a enviar todo dentro del "value1" separado por |||
// Con este truco podemos enviar más de 3 valores (limitado por IFTTT)
// ya que la hoja de Google va a interpretar ||| como tabulación
String url = "/trigger/";
url += evento;
url += "/with/key/";
url += key;
url += "?value1=";
url += valor1;
url += "|||";
url += valor2;
url += "|||";
url += valor3;
url += "|||";
url += valor4;
url += "|||";
url += valor5;
Serial.print("Solicitando URL: "); // mostramos por el monitor serie la URL que vamos a utilizar
Serial.println(url);
// Realizamos la petición GET
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
// Leemos la respuesta y la pintamos en pantalla
while (client.available()) {
char c = client.read();
Serial.write(c);
}
Serial.println(); // espaciamos un poco la salida por el monitor serie
// Desconexion
if (client.connected()) {
Serial.println();
Serial.println("Desconectando del servidor...");
client.flush();
client.stop();
}
}
}
// Esta función emite un tono por el zumbador activo.
// Podemos variar el tono enviando un retardo mayor o menor
void tono(int retardo) {
unsigned char i;
for (i = 0; i < 100; i++)
{
digitalWrite(PIN_ZUMBADOR, HIGH);
delay(retardo);
digitalWrite(PIN_ZUMBADOR, LOW);
delay(retardo);
}
}
// esta función muesta información por pantalla del estado wifi. Interesante si se quieren depurar errores.
// void verEstadoWifi()
//{
// // SSID al que nos hemos conectado
// Serial.print("SSID: ");
// Serial.println(WiFi.SSID());
//
// // la IP asignada
// IPAddress ip = WiFi.localIP();
// Serial.print("IP: ");
// Serial.println(ip);
//
// // fuerza de la señal
// long rssi = WiFi.RSSI();
// Serial.print("Señar recibida (RSSI):");
// Serial.print(rssi);
// Serial.println(" dBm");
//}