Implementación de Código Solución - ferestradaa/RetoIOT GitHub Wiki

Versiones de código solución completo


Código para red WPA2

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


Código para red personal

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.


Versión documentada y explicada del código solución en Arduino

¿Qué tiene que hacer el programa?

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:


1. Incluir librerías y definir identificadores

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);

2. Declaración de variables

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:

  1. Leer el número que es enviado a la base de datos
  2. 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

3. Verificación de conexión a red inalámbrica WIfi protegida

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

4. Implementación de SETUP!

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);

5. Definición de funciones para sensores

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;          
    }
}

6. Implementación de LOOP!

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.


Resultados

Ejecutando el código, se obtienen los siguientes resultados: Resultado del Serial Monitor en Arduino IDE

Screenshot 2022-11-28 at 11 56 12

Resultados en la Base de Datos

Screenshot 2022-11-28 at 11 57 40

Resultado en el display

WhatsApp Image 2022-11-28 at 14 37 18

Circuito completo

WhatsApp Image 2022-11-28 at 13 53 50

⚠️ **GitHub.com Fallback** ⚠️