Interface web para el ESP8266 o ESP32 con Vue y MQTT Deja un comentario

Nueva entrada sobre el ESP8266/ESP32 dedicada a ver formas de comunicación donde vamos a ver cómo hacer un interface Web controlado a través de MQTT.

Y con esta entrada (por fin!) terminamos esta serie de 40 tutoriales y más de tres añazos en los que hemos visto todas las formas de comunicación con un ESP8266/ESP32, desde peticiones HTTP a MQTT, pasando por llamadas Ajax y Websockets, entre otros.

¡Pero tranquilos! Esto no significa que sea el final de los tutoriales sobre el ESP32 en el blog. Al contrario, nos queda mucho mucho de que hablar sobre nuestro amigo el ESP32. Ahora que hemos terminado esta (larguísima) serie de entradas ¡estamos libres para hablar muchas más cosas!

Y como no podía ser de otra cosa, vamos a cerrar con nuestro ejercicio “estrella”, que como sabemos simular un proyecto de control web completo del ESP8266/ESP32. Ya vimos este mismo proyecto en su versión de Websocket y ahora toca hacerlo a través de MQTT.

Anuncio:

Así que sín más marear la perdiz, ¡vamos al lio!. En este caso, nuestro fichero principal es muy similar al que ya teníamos en el caso de Websockets, unicamente cambian los #includes. Si tenéis alguna duda, echarle un ojo al tutorial del caso websockets.

#include 
#include 
#include 
#include 
#include 

#include "config.h" // Sustituir con datos de vuestra red
#include "API.hpp"
#include "MQTT.hpp"
#include "Server.hpp"
#include "ESP32_Utils.hpp"
#include "ESP32_Utils_MQTT_Async.hpp"
#include "ReactiveArduinoLib.h"

auto obsD0 = Reactive::FromDigitalInput(0);
auto obsD5 = Reactive::FromDigitalInput(5);
auto obsD6 = Reactive::FromDigitalInput(6);
auto obsD7 = Reactive::FromDigitalInput(7);

void setup(void)
{
	Serial.begin(115200);
	SPIFFS.begin();

	delay(500);

	WiFi.onEvent(WiFiEvent);
	InitMqtt();

	ConnectWiFi_STA();
	InitServer();

	obsD0.Distinct().Do([](int i) { updateGPIO("D0", i); });
	obsD5.Distinct().Do([](int i) { updateGPIO("D5", i); });
	obsD6.Distinct().Do([](int i) { updateGPIO("D6", i); });
	obsD7.Distinct().Do([](int i) { updateGPIO("D7", i); });
}

void loop()
{
	obsD0.Next();
	obsD5.Next();
	obsD6.Next();
	obsD7.Next();
}

En el fichero API.hpp tenemos las funciones que emulan las acciones a realizar al pulsar al interface.

void setGPIO(String id, bool state)
{
   Serial.print("Set GPIO ");
   Serial.print(id);
   Serial.print(": ");
   Serial.println(state);
}

void setPWM(String id, int pwm)
{
   Serial.print("Set PWM ");
   Serial.print(id);
   Serial.print(": ");
   Serial.println(pwm);
}

void doAction(String actionId)
{
   Serial.print("Doing action: ");
   Serial.println(actionId);
}

Por tro lado, el fichero MQTT.hpp contiene toda la lógica asociada a la comunicación del proyecto.

#pragma once

const IPAddress MQTT_HOST(192, 168, 1, 150);
const int MQTT_PORT = 1883;

AsyncMqttClient mqttClient;

String GetPayloadContent(char* data, size_t len)
{
	String content = "";
	for(size_t i = 0; i < len; i++)
	{
		content.concat(data[i]);
	}
	return content;
}

void SuscribeMqtt()
{
	uint16_t packetIdSub = mqttClient.subscribe("device/0/#", 0);
	Serial.print("Subscribing at QoS 2, packetId: ");
	Serial.println(packetIdSub);
}

void updateGPIO(String input, bool value)
{
	String payload;
	StaticJsonDocument<300> doc;
	doc["command"] = "updateGPIO";
	doc["id"] = input;
	doc["status"] = value;
	serializeJson(doc, payload);

	mqttClient.publish("device/0/GPIO", 0, true, (char*)payload.c_str());

	Serial.print(input);
	Serial.println(value ? String(" ON") : String(" OFF"));
}

void OnMqttReceived(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
{
	Serial.print("Received on ");
	Serial.print(topic);
	Serial.print(": ");

	String content = GetPayloadContent(payload, len);

	StaticJsonDocument<200> doc;
	DeserializationError error = deserializeJson(doc, content);
	if(error) return;

	int id = doc["id"];
	bool ledStatus = doc["status"];

	String command = doc["command"];
	if(content.indexOf("GPIO") > 0 && command == "setGPIO")
		setGPIO(doc["id"], (bool)doc["status"]);
	else if(content.indexOf("PWM") > 0 && command == "setPWM")
		setPWM(doc["id"], (int)doc["pwm"]);
	else if(content.indexOf("Action") > 0 && command == "doAction")
		doAction(doc["id"]);
	else
	{
		//otras acciones
	}
}

Por el lado del cliente, tenemos la página ‘index.html’, que contiene la definición de nuestro interface.





  ESP32 VueJS
  
  
  
  
  



  
  
  

  
    Mqtt ESP32
    
      

        
          
            
              
                Input example
              
              
                  
              
            
          

          
              
                
                  Output example
                
                
                    
                
              
            

            
                
                  
                    PWM example
                  
                  
                      
                  
                
              

              
                  
                    
                      Actions example
                    
                    
                        
                    
                  
                
        
      
    
  

  
  
  
  
  
  

  
  
  


La “chicha” de nuestra web está en el fichero App.js que contiene la lógica de nuestra aplicación

Vue.component('gpio-input', {
  props: ['gpio'],
  template: `
    
      
        {{gpio.text}}
      
    
      {{ gpio.status ? "ON " : "OFF "}}
      fiber_manual_record
    
    
    `
})

Vue.component('gpio-output', {
  props: ['gpio'],
  template: ` 
    
      
        {{gpio.text}}
      
      
        
      
    
`,
  methods: {
    sendGPIO: function (evt) {
      console.log(this.gpio.text + ': ' + this.gpio.status);

      let data = {
        command: "setGPIO",
        id: this.gpio.text,
        status: this.gpio.status
      }

      let topic = 'device/0/GPIO/';
      let payload = JSON.stringify(data);
      let pubQoS = 0;
      let retain = false;
      client.send(topic, payload, Number(pubQoS), retain);
    }
  }
})

Vue.component('pwm', {
  props: ['gpio'],
  template: `     
      
        
          {{gpio.text}}
        
        
        
            
              
        
      `,
  methods: {
    sendPWM: function (evt) {
      console.log(this.gpio.text + ': ' + this.gpio.value);

      let data = {
        command: "setPWM",
        id: this.gpio.text,
        pwm: this.gpio.value
      }

      let topic = 'device/0/PWM/';
      let payload = JSON.stringify(data);
      let pubQoS = 0;
      let retain = false;
      client.send(topic, payload, Number(pubQoS), retain);
    }
  }
})

Vue.component('action', {
  props: ['action'],
  template: ` 
        
        
          {{action.text}}
        
      
      Do something
      
      
`,
  methods: {
    doAction: function (evt) {
      console.log(this.action.text + ': ' + this.action.id);
      let data = {
        command: "doAction",
        id: this.action.id,
      }

      let topic = 'device/0/Action/';
      let payload = JSON.stringify(data);
      let pubQoS = 0;
      let retain = false;
      client.send(topic, payload, Number(pubQoS), retain);
    }
  }
})

var app = new Vue({
  el: '#app',
  data: function () {
    return {
      gpio_input_list: [
        { id: 0, text: 'D0', status: 0 },
        { id: 1, text: 'D5', status: 0 },
        { id: 2, text: 'D6', status: 0 },
        { id: 3, text: 'D7', status: 0 },
      ],
      gpio_output_list: [
        { id: 0, text: 'D8', status: 1 },
        { id: 1, text: 'D9', status: 0 },
        { id: 2, text: 'D10', status: 0 },
      ],
      pwm_list: [
        { id: 0, text: 'PWM1', value: 128 },
        { id: 1, text: 'PWM2', value: 128 },
      ],
      action_list: [
        { id: 0, text: 'ACTION1', callback: () => console.log("action1") },
        { id: 1, text: 'ACTION2', callback: () => console.log("action2") },
      ]
    }
  },
  mounted() {
    client = new Paho.MQTT.Client("192.168.1.150", 9001, createGuid())
    var options = {
      onSuccess: onConnect,
      onFailure: onFailure
    };
    client.onConnectionLost = onConnectionLost;
    client.onMessageArrived = onMessageArrived;
    client.connect(options);
  }
})

Finalmente tenemos el fichero API.js, que contiene las funciones propias de MQTT usadas en el lado cliente.

function onConnect() {
  var options = {
    qos: 0,
    onSuccess: onSubSuccess,
    onFailure: onSubFailure
  };
  client.subscribe('device/0/#', options);
}

function onFailure(message) {
  console.log(message)
}

function onConnectionLost(responseObject) {
  if (responseObject.errorCode !== 0) {
    console.log("onConnectionLost:" + responseObject.errorMessage);
  }
}

function onMessageArrived(message) {
  console.log(message)
  var topic = message.destinationName;
  var json = message.payloadString;

  let payload = JSON.parse(json);
  console.log(payload)
  if(payload.command.includes("updateGPIO"))
  {
    let gpio = app.gpio_input_list.find(gpio => gpio.text == payload.id);
    gpio.status = payload.status;
  }
}

function onSubFailure(message) {
  console.log(message)
}

function onSubSuccess(message) {
  console.log(message)
}

function createGuid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    var r = Math.random() * 16 | 0,
      v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

Subimos todo eso a nuestro ESP8266/ESP32 y si todo ha salido bien, deberíamos ver el interface de nuestro proyecto.

Asímismo, comprobamos que al realizar las acciones, estas se reciben y se muestran en puerto serie. De igual forma, si realizamos un cambio de estado en uno de los pines, veremos reflejado el cambio de estado en la aplicación del cliente.

¡Y con esto hemos terminado! Como decíamos al principio de esta entrada, terminamos esta serie entradas sobre comunicación con el ESP32. Aún nos quedan muchos más tutoriales del ESP32 por ver… Pero, al que sí le vamos a decir adiós es nuestro amigo el ESP8266. El pobrecito hace un tiempo que fue declarado obsoleto por el fabricante Espressif, por lo que de ahora en adelante el resto de entradas del blog serán sólo para el ESP32.

Nos vemos en la próxima entrada, y mientras tanto os dejo aquí todo el código de estas y las anteriores entradas para que les echéis un ojo con más calma. ¡Hasta pronto!

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github.

Versión para el ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples

Versión para el ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples

Si te ha gustado esta entrada y quieres leer más sobre ESP8266 o el ESP32 puedes consultar la sección tutoriales de ESP8266/32
0 0 votes
Article Rating

Anuncio:

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

0
    0
    Tu Carrito
    Tu carrito esta vacíoVolver a la tienda
    Enviar Whatsapp
    Hola 👋
    ¿En qué podemos ayudarte?