Implementación de Código Solución - ferestradaa/RetoIOT GitHub Wiki
En este código se encuentra la solución final del Reto de Implementación de internet de las cosas; para la red protegida en el protocolo WPA2, tal como es la del Tecnológico de Monterrey
En esta segunda versión del código se encuentra la solución final del Reto de Implementación de internet de las cosas; para la red de conexión inalámbrica personal.
Se espera que el programa de Arduino sea capaz de leer y enviar 5 variables físicas proporcionadas por sensores los cuales envíen constantemente información a la base de datos en FireBase. Además, existe una sexta y séptima variable (usuario y numero) que son proporcionadas mediante una aplicación móvil a través de programación por bloques en la plataforma AppInventor. Para este rubro, si el usuario ingresa un numero entre 0 y 9, este deberá desplegarse en el display de 7 segmentos, en caso contrario, únicamente se desplegara un "-".
Por otra parte, este código de Python también interactúa con esta variable leída en la base de datos, pues si el valor de numero excede 9, se imprime un mensaje en la base de datos indicado esta condición.
A continuación se muestra la explicación del código final:
Las siguientes líneas tienen como objetivo incluir las librerías y extensiones necesarias para hacer funcionar la placa ESP32 con firebase. Por ello, la mayor parte de ellas se incluyen para realizar la conexión a Wifi protegida de la placa y simultáneamente a firebase. Entre ellas destacan la <esp_wpa2.h> y <Firebase_ESP_Client.h>.
Además, se puede observar que se definen ciertos rubros como identificadores del sensor, esto es necesario ya que en primera instancia, se le debe indicar a Arduino que un pin digital en particular del sensor será utilizado.
#include <esp_wpa2.h>
#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include <addons/TokenHelper.h>
#include "Arduino.h"
#include "addons/RTDBHelper.h"
#include "DHT.h"
//definicion de sensor DHT11
#define DHTPIN 4
#define DHTTYPE DHT11 // DHT 11
DHT dht(DHTPIN, DHTTYPE);
En esta sección es sumamente importante declarar variables que se utilizarán directamente en la función de un sensor o bien, aquellas que almacenarán información que será enviada a la base de datos.
- Variables para sensor de temperatura y humedad (DHT11)
//------Sensor de Temperatura
float h;
float t;
float f;
float hif;
float hic;
- Variables y declaración de pines digitales para sensor ultrasónico (SCR)
//------Sensor de Temperatura
const int trigPin = 5; //pines a utilizar (5 y 18)
const int echoPin = 18;
Definición de velocidad del sonido en cm/Us
//declarar velocidad del sonido en cm/uS
#define SOUND_SPEED 0.034
#define CM_TO_INCH 0.393701
Variables a utilizar
long duration;
float distanceCm;
float distanceInch;
- Variables y declaración de pines digitales para sensor de movimiento (SCR)
En esta sección se encuentran definidas variables que tienen un valor digital asignado (LOW), dado que se trabajará con variables booleanas, este estado permite monitorear el funcionamiento del sensor de la misma forma, ya sea true o false el resultado de la ejecución.
//-------Sensor de movimiento
const int PIN_TO_SENSOR = 19; // GIOP19 pin conectado al OUTPUT pin del sensor
int pinStateCurrent = LOW; // estado actual del pin
bool cond; //variable que indica la alerta de movimiento
- Variables y declaración de pines digitales para sensor de flama (IR Infrared)
Para este sensor también se trabajará con variables booleanas.
//-------Sensor de flama
const int sensor = 13; // Pin a utlizar para el sensor
int pinStateCurrent2 = LOW;
bool advise; //variable que indica si hay fuego o no
- Variables y declaración de pines digitales para display de 7 segmentos
En este componente se encuentra particularmente estructurado el programa. De entrada se define un arreglo con 7 elementos los cuales están vinculados con los pines a conectar de la placa. Por lo que es muy importante que se asignen aquellos que no han sido utilizados con anterioridad. Cada pin corresponde a un segmento específico del display.
//-------Display de 7 segmentos
int LEDs[] = {21,23,25,26,27,14,12}; //arreglo para declarar los pines a usar del display
//------------g f e d c b a // representacion de cada pin en tal orden
Posteriormente se declaran nuevos arreglos que forman los distintos números que pueden desplegarse. Dependiendo de la posición que este prendida de acuerdo con el orden de los segmentos, es que se representan los dígitos. Por ejemplo, para representar el 0, es necesario que todos los pines esten encendidos menos el del centro.
Conjuntamente, se incluyen 2 variables de tipo string y entero respectivamente, las cuales servirán para:
- Leer el número que es enviado a la base de datos
- Representarlo en el display tras convertirlo de tipo string a int
//se declaran los arreglos que forman los dígitos
int zero[] = {0, 1, 1, 1, 1, 1, 1}; // cero
int one[] = {0, 0, 0, 0, 1, 1, 0}; // uno
int two[] = {1, 0, 1, 1, 0, 1, 1}; // dos
int three[] = {1, 0, 0, 1, 1, 1, 1}; // tres
int four[] = {1, 1, 0, 0, 1, 1, 0}; // cuatro
int five[] = {1, 1, 0, 1, 1, 0, 1}; // cinco
int six[] = {1, 1, 1, 1, 1, 0, 1}; // seis
int seven[] = {0, 0, 0, 0, 1, 1, 1}; // siete
int eight[] = {1, 1, 1, 1, 1, 1, 1}; // ocho
int nine[] = {1, 1, 0, 1, 1, 1, 1}; // nueve
int def[] = {1, 0, 0, 0, 0, 0, 0}; // nueve
String numero; //variable para leer en firebase
int num; // variable para almacenar el numero que de desplegara en el display
Esta sección del código es la que se encarga principalmente de conectar la placa ESP32 a una red protegida para posteriormente realizar una autenticación de usuario para tener acceso a los datos de firebase. Con dicha conexión habilitada, será posible actualizar la información de la base de datos constantemente.
//Verificacion de conexion a red inalambrica Wi-fi protegida
const char* ssid = "Tec";
#define EAP_IDENTITY "[email protected]"
#define EAP_PASSWORD "SuContraseña"
Para ingresar como usario a firebase:
//Auntenticadores para firebase
#define USER_EMAIL "[email protected]"
#define USER_PASSWORD "12345678"
Para ingresar a la base de datos deseada, es necesario ingresar sus elementos principales, tal como lo son la API key y el URL de la base:
// API Key del proyecto en Firebase
#define API_KEY "AIzaSyAYHUhONVse4aLsBPpbSN1URs8KxTNdNgQ"//AIzaSyAjjTHMIV0y394tayvijhU-aVVcKdkIZxU
// URL de la base de datos en firebase
#define DATABASE_URL "https://pryecto1-b58cc-default-rtdb.firebaseio.com/"
Finalmente, se realiza la validación del sign up y los objetos de FireBase:
//Define Firebase Data object
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
unsigned long sendDataPrevMillis = 0;
int intValue;
float floatValue;
bool signupOK = false; //validacion de sign up en firebase
El setup en Arduino es una sección reservada del código en la que básicamente el sketch incia. Esta sección solo es ejecutada una vez. A continuación se muestra el proceso completo para realizar la conexión a Wifi y el sign up a Firebase
//Conexion a wifi
Serial.begin(115200); //Abertura un Puerto serie y especifica la velocidad de transmisión.
dht.begin();
delay(10);
Serial.println();
Serial.print("Connecting to "); //comienza la validacion para conectarse a la red de internet
Serial.println(ssid); //ssid (nombramiento) de la red
// WPA2 enterprise magic starts here
WiFi.disconnect(true);
WiFi.mode(WIFI_STA); //init wifi mode
//esp_wifi_set_mac(ESP_IF_WIFI_STA, &masterCustomMac[0]);
Serial.print("MAC >> ");
Serial.println(WiFi.macAddress());
Serial.printf("Connecting to WiFi: %s ", ssid);
//esp_wifi_sta_wpa2_ent_set_ca_cert((uint8_t *)incommon_ca, strlen(incommon_ca) + 1);
esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY));
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY));
esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EAP_PASSWORD, strlen(EAP_PASSWORD));
//esp_wpa2_config_t configW = WPA2_CONFIG_INIT_DEFAULT();
//esp_wifi_sta_wpa2_ent_enable(&configW);
esp_wifi_sta_wpa2_ent_enable();
// WPA2 enterprise magic ends here
WiFi.begin(ssid);
Mientras inicia la conexión, se imprimirá una barra de espera "." hasta que se logre dicha conexión
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Posteriormente, una vez conectado, se imprime el mensaje junto con la IP de la red, seguido de un mensaje de proceso completado "ok!" y por lo tanto, la variable booleana de sign up en true
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
///* Assign the api key (required) */
config.api_key = API_KEY;
/* Assign the RTDB URL (required) */
config.database_url = DATABASE_URL;
/* Sign up */
if (Firebase.signUp(&config, &auth, "", "")){
Serial.println("ok!");
signupOK = true;
}
else{
Serial.printf("%s\n", config.signer.signupError.message.c_str());
}
/* Assign the callback function for the long running token generation task */
config.token_status_callback = tokenStatusCallback; //see addons/TokenHelper.h
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
Por otra parte, se deben inicializar los pines de salida del display de 7 segmentos:
for (int i = 0; i<7; i++) pinMode(LEDs[i], OUTPUT);
Con las variables que fueron inicializadas al principio del programa junto con la declaración de pines, ahora es posible construir toda una función que haga funcionar a los sensores correctamente, imprimiendo sus lecturas en primera instancia dentro del Serial Monitor.
- Función para sensor de temperatura
void temperatura(){
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
h = dht.readHumidity(); // leer temperatura en Celsius (the default)
t = dht.readTemperature(); // Read temperature as Fahrenheit (isFahrenheit = true)
f = dht.readTemperature(true);
// Compute heat index in Fahrenheit (the default)
hif = dht.computeHeatIndex(f, h);
// Compute heat index in Celsius (isFahreheit = false)
hic = dht.computeHeatIndex(t, h, false);
}
- Función para sensor de distancia
void distancia(){
pinMode(trigPin, OUTPUT); // se establece el trigPin como Output
pinMode(echoPin, INPUT); // se establece el echoPin como Input}
// Clears the trigPin
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// leyendo el echoPin, regresa el tiempo de camino de la onda de sonido
duration = pulseIn(echoPin, HIGH);
// calcula la disctancia con la duracion y la velocidad del sonido
distanceCm = duration * SOUND_SPEED/2;
// Conversion a pulgadas
distanceInch = distanceCm * CM_TO_INCH;
//Se imprime el valor de la distancia en el serial monitor
Serial.print("Distance (cm): ");
Serial.println(distanceCm);
Serial.print("Distance (inch): ");
Serial.println(distanceInch);
delay(1000);
}
- Función para sensor de movimiento
void movimiento(){
pinMode(PIN_TO_SENSOR, INPUT); // se establece un pin a input para leer el valor de output de un pin del sensor
//pinStatePrevious = pinStateCurrent; // store old state
pinStateCurrent = digitalRead(PIN_TO_SENSOR); // se lee el estado actual
if(pinStateCurrent == HIGH) { //si el estado actual del pin es HIGH
cond = true; //variable booleana que valida el estado del pin
Serial.print("Motion!"); //se imprime movimiento detectado
delay(1000);
}
else if(pinStateCurrent == LOW){ //caso contrario
// pin state change: HIGH -> LOW
cond = false;
Serial.print("No Motion detected");
delay(1000);
}
}
- Función para sensor de flama
void flama(){
pinMode(sensor,INPUT); //declarar pin de entrada
pinStateCurrent2 = digitalRead(sensor); //lectura de estado actual
if (pinStateCurrent2 == HIGH) { //si el estado actual del pin es HIGH
//advise = Serial.print("FIRE DETECTED!\n");
advise = true; //variable booleana para validad la activacion del sensor
Serial.print("Fire!"); //se imprime fuego detectado
delay(1000);
}
else if (pinStateCurrent2 == LOW){ //caso contrario
//advise = Serial.println("FIRE NOT DETECTED");
advise = false;
Serial.print("\nNo Fire detected");
delay(1000);
}
}
- Función para display de 7 segmentos
void segment_display(unsigned char valor){
switch(valor) //indicar HIGH O LOW para cada pin a encender dependiendo del numero leido.
{
case 0:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], zero[i]);
break;
case 1:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], one[i]);
break;
case 2:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], two[i]);
break;
case 3:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], three[i]);
break;
case 4:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], four[i]);
break;
case 5:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], five[i]);
break;
case 6:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], six[i]);
break;
case 7:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], seven[i]);
break;
case 8:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], eight[i]);
break;
case 9:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], nine[i]);
break;
default:
for (int i = 0; i<7; i++) digitalWrite(LEDs[i], def[i]);
break;
}
}
En esta sección se encuentra el proceso que se va a ejecutar constantemente hasta que se le indique al programa, por lo que se encontrará en un bucle. Por esta razón, la información mostrada por los sensores estará cambiando periódicamente de acuerdo a las condiciones a las que sean sometidos.
Por ello, una vez que han sido declaradas las funciones, es necesario llamarlas en el loop, tal como se muestra a continuación:
temperatura(); //llamado de funciones de los sensores
distancia();
movimiento();
flama();
Posteriormente, una vez con los sensores funcionando, los datos que transmitan deben ser enviados a la base de datos en Firebase, por lo que, si se han cumplido al momento las condiciones de conectividad, verificación y autentificación, entonces los datos pueden ser enviados.
if (Firebase.ready() && signupOK && (millis() - sendDataPrevMillis > 15000 || sendDataPrevMillis == 0)) {
sendDataPrevMillis = millis(); //en caso de que el sign in sea exitoso y la conexion a internet, se mandan los datos a firebase
- Envío de información de sensor de temperatura y humedad mediante variables hic, hif y h.
En caso de que ejecutarse correctamente, la información será enviada a la base a una sección en particular con nombre Información. Para el caso de este sensor, los datos se mandarán en grados C°, F, y humedad, cada uno mediante una variable distinta.
if (Firebase.RTDB.setFloat(&fbdo, "Informacion/Temperatura C", hic)){ //hic se manda a firebase
Simultáneamente se imprimirá en el Serial Monitor el mensaje de: (para cada sensor)
Serial.println("\n\nPASSED TEMP C: ");
En caso contrario, se imprimirá el estado fallido de la transmisión de datos, junto con el mensaje de error que lo impide:
else {
Serial.println("FAILED TEMP C"); //si no es posible, se imprime el error de por que
Serial.println("REASON: " + fbdo.errorReason());
El mismo procedimiento se repite para los demás sensores
- Envío de información de sensor de distancia mediante variables distanceCm y distanceInch
Nuevamente se envían dos lecturas distintas, las distancias medidas en centímetros y pulgadas, respectivamente.
if (Firebase.RTDB.setFloat(&fbdo, "Informacion/Distancia en cm", distanceCm)){ //distanceCM se manda a firebase
Serial.println("PASSED DISTANCE CM");
}
else {
Serial.println("FAILED");
Serial.println("REASON: " + fbdo.errorReason());
}
if (Firebase.RTDB.setFloat(&fbdo, "Informacion/Distancia en inch", distanceInch)){ //distanceInch se manda a firebase
Serial.println("PASSED DISTANCE INCH");
}
else {
Serial.println("FAILED");
Serial.println("REASON: " + fbdo.errorReason());
}
- Envío de información de sensor de movimiento mediante variable cond
Este sensor utiliza una variable booleana, por lo que su lectura lanzará un true o false dependiendo del registro de movimiento.
if (Firebase.RTDB.setBool(&fbdo, "Informacion/Movimiento", cond)){ //cond se manda a firebase
Serial.println("PASSED MOTION");
}
else {
Serial.println("FAILED MOTION");
Serial.println("REASON: " + fbdo.errorReason());
}
- Envío de información de sensor de flama mediante variable advise
El sensor de flama también funciona con variable booleana, entonces solo se indicará si se detecta fuego o no.
if (Firebase.RTDB.setBool(&fbdo, "Informacion/Flama", advise)){ //advise se manda a firebase
Serial.println("PASSED FLAME\n");
}
else {
Serial.println("FAILED FLAME");
Serial.println("REASON: " + fbdo.errorReason());
}
- Lectura de información de display de 7 segmentos mediante variable numero
En este caso será contrario el proceso, pues en lugar de enviar una variable, esta será leida y posteriormente desplegada en el display. Sin embargo, antes de desplegarse es necesario que sea convertida a un valor entero, ya que por defecto la aplicación lo manda como un valor de cadena.
if (Firebase.RTDB.getString(&fbdo, "/test/numero")){ //se lee un numero de la base de datos con getString
numero = fbdo.stringData(); //se lee de la varaible numerol, la cual se manda con la aplicacion en forma de string
num = numero.toInt(); //se convierte el valor a entero
}
segment_display(num);//se manda a llamar a la funcion de display con el nuemero leido
}
Nótese que se ha cambiado el comando a getString de Firebase, dato que se está leyendo el dato.
Ejecutando el código, se obtienen los siguientes resultados: Resultado del Serial Monitor en Arduino IDE
Resultados en la Base de Datos
Resultado en el display