como-comunicar-app-en-vuejs-con-esp32-a-traves-de-mqtt

How to communicate App in VueJs with ESP32 via MQTT

  • 5 min

A Vue application can consume MQTT messages received from an ESP32 through WebSockets.

In this example we start from a web page served from the ESP32 itself and use the PAHO library so that the frontend can connect to the MQTT broker through WebSockets.

Until now we could solve it with simple JavaScript: read the JSON message and add the received value to the page. But if the interface starts to grow, it makes sense to integrate it into a VueJs application.

While we are at it, let us modify the example so that, instead of simply sending the value of millis(), it publishes more interesting information. For example, the state of a GPIO, simulating the reception of an alarm.

In short, we are going to do something like this.

esp32-mqtt-json-app-vue-resultado

However, in this example the GPIO state will be simulated, because we are only interested in illustrating the communication. It’s up to you to adapt it to the hardware and needs of your project.

So let’s get to work.

The main loop of the program keeps the same basic structure as in the previous MQTT examples.

#include <WiFi.h>
#include <SPIFFS.h>
#include <ESPAsyncWebServer.h>
#include <AsyncMqttClient.h>
#include <ArduinoJson.h>

#include "config.h"  // Replace with data from your network
#include "Server.hpp"
#include "MQTT.hpp"
#include "ESP32_Utils.hpp"
#include "ESP32_Utils_MQTT_Async.hpp"

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

  delay(500);

  WiFi.onEvent(WiFiEvent);
  InitMqtt();

  ConnectWiFi_STA();
  InitServer();
}

void loop()
{
  PublishMqtt();

  delay(1000);
}
Copied!

What has changed is the file ‘MQTT.hpp’, where we have separated the logic associated with MQTT communication from our program. There aren’t major modifications either.

We have only changed the Json we send to contain the GPIO Id and the status. For the example, we are simply filling it with random values.

#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("hello/world", 0);
  Serial.print("Subscribing at QoS 2, packetId: ");
  Serial.println(packetIdSub);
}

void PublishMqtt()
{
  String payload = "";

  // We would get GPIO data, status...
  StaticJsonDocument<300> jsonDoc;
  jsonDoc["id"] = random(0, 10);
  jsonDoc["status"] = random(0, 2);
  serializeJson(jsonDoc, payload);

  mqttClient.publish("hello/world", 0, true, (char*)payload.c_str());
}

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"];

  Serial.print(" Id:");
  Serial.print(id);
  Serial.print(" Status:");
  Serial.println(ledStatus);
}
Copied!

What will change substantially is the frontend we serve to the client, which, as we said, will become a VueJs App.

The ‘index.html’ file becomes the following.

<!doctype html>
<html lang="">

<head>
  <title>ESP32 MQTT</title>
  <meta charset="utf-8">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <title></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  <link rel="stylesheet" href="css/main.css">
  <link href="./vendor/google-fonts.css" rel="stylesheet">
  <link href="./vendor/vuetify.min.css" rel="stylesheet">

  <v-app id="app">
    <v-toolbar app>ESP32 MQTT</v-toolbar>
    <v-content>
      <v-container fluid grid-list-md text-xs-center>

        <v-layout row wrap>
          <v-flex>
            <v-card>
              <v-toolbar color="blue" dark>
                <v-toolbar-title class="text-xs-center">Received</v-toolbar-title>
              </v-toolbar>

              <v-timeline
              align-top
              dense
            >
            <mqtt-message v-for="item in mqtt_message_list" :mqtt_message="item" />
            </v-timeline> 
            </v-card>
          </v-flex>

        </v-layout>
      </v-container>
    </v-content>
  </v-app>

  <!-- From CDN -->
  <script type="text/javascript" src="./vendor/vue.min.js"></script>
  <script type="text/javascript" src="./vendor/vuetify.min.js"></script>
  <script type="text/javascript" src="./vendor/nativeWs.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>

  <!-- Load the file containing our Vue.JS App -->
  <script type="text/javascript" src="./js/API.js"></script>
  <script type="text/javascript" src="./js/app.js"></script>
</body>

</html>
Copied!

On the other hand, we have the ‘API.js’ file which contains the frontend logic associated with MQTT communication.

function onConnect() {

    var options = {
      qos: 0,
      onSuccess: onSubSuccess,
      onFailure: onSubFailure
    };
    client.subscribe('hello/world', 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 payload = message.payloadString;
  
    let json = JSON.parse(payload);
  
    var mqtt_message = new Object();
    mqtt_message.id = json.id;
    mqtt_message.status = json.status;
    mqtt_message.date = new Date().toISOString().replace("T", " ").replace("Z", " ");
    app.mqtt_message_list.unshift(mqtt_message);
  }
  
  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);
    });
  }
Copied!

Finally, the ‘App.js’ file contains the Vue application, and looks like this.

  Vue.component('mqtt-message', {
    props: ['mqtt_message'],
    template: `
        <v-timeline-item
        color="blue"
        small
        >
        <v-layout pt-3>
            <v-flex xs3>
            {{mqtt_message.date}}
            </v-flex>
            <v-flex>
            <strong>GPIO: {{mqtt_message.id}}</strong>
            <div class="caption">Status: {{mqtt_message.status}}</div>
            </v-flex>
        </v-layout>
        </v-timeline-item>
    `
})

var app = new Vue({
    el: '#app',
    data: function () {
        return {
            mqtt_message_list: [

            ]
        }
    },
    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);
    },
})
Copied!

Result

We upload all this to our ESP32 and we will see that the simulated messages are received every second and added to the alert list.

esp32-mqtt-json-app-vue-resultado

Similarly, in the ESP32 serial port we also see the received messages. In this case, they are sent by the device itself, to avoid having to use several. But, if we had several devices, they would all receive simultaneously.

esp32-mqtt-json-app-vue-serial

With this we have a Vue application that consumes MQTT data from the browser. From this base we can expand the panel with more states, commands, and actions, keeping MQTT as the common channel between the ESP32, the web page, and the rest of the system.

Download the Code

All the code from this post is available for download on Github.

github-full

Version for ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples

Version for ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples