domingo, 23 de julio de 2017

Tutorial #11 ESP8266 (Parte1) - MQTT + CloudMQTT

Este tutorial de IOT (internet de las cosas) es la primera parte de una serie de tutoriales sobre MQTT en ESP8266, para esto nos valemos del servicio gratuito de CloudMQTT.com

Vamos a usar exactamente el mismo hardware del tutorial 5(.../tutorial-5-esp8266-mqtt-ioadafruitcom.html), donde tenemos una entrada digital (pulsador), una entrada analógica (sensor de temperatura LM35), una salida digital (LED Rojo) y una salida analógica PWM (LED Verde).

En el video muestro como controlar la placa desde la consola de CloudMQTT; en próximos tutoriales voy a explicar cómo controlarlo desde una aplicación de Android, hacer nuestros propios tableros usando WebSocket, controlarlo desde Python, etc.



Código Fuente:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

//-------------------VARIABLES GLOBALES--------------------------
int contconexion = 0;

const char *ssid = "---";
const char *password = "---";
char   SERVER[50]   = "75.101.131.202"; //"m11.cloudmqtt.com"
int    SERVERPORT   = 10722;
String USERNAME = "placa1";   
char   PASSWORD[50] = "12345678";     

unsigned long previousMillis = 0;

char charPulsador [15];
String strPulsador;
String strPulsadorUltimo;

char PLACA[50];

char valueStr[15];
String strtemp = "";
char TEMPERATURA[50];
char PULSADOR[50];
char SALIDADIGITAL[50];
char SALIDAANALOGICA[50];


//-------------------------------------------------------------------------
WiFiClient espClient;
PubSubClient client(espClient);

//------------------------CALLBACK-----------------------------
void callback(char* topic, byte* payload, unsigned int length) {

  char PAYLOAD[5] = "    ";
  
  Serial.print("Mensaje Recibido: [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    PAYLOAD[i] = (char)payload[i];
  }
  Serial.println(PAYLOAD);

  if (String(topic) ==  String(SALIDADIGITAL)) {
    if (payload[1] == 'N'){
     digitalWrite(12, HIGH);
    }
    if (payload[1] == 'F'){
      digitalWrite(12, LOW);
    }
  }

  if (String(topic) ==  String(SALIDAANALOGICA)) {
    analogWrite(13, String(PAYLOAD).toInt());
  }
}

//------------------------RECONNECT-----------------------------
void reconnect() {
  uint8_t retries = 3;
  // Loop hasta que estamos conectados
  while (!client.connected()) {
    Serial.print("Intentando conexion MQTT...");
    // Crea un ID de cliente al azar
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    USERNAME.toCharArray(PLACA, 50);
    if (client.connect("", PLACA, PASSWORD)) {
      Serial.println("conectado");
      client.subscribe(SALIDADIGITAL);
      client.subscribe(SALIDAANALOGICA);
    } else {
      Serial.print("fallo, rc=");
      Serial.print(client.state());
      Serial.println(" intenta nuevamente en 5 segundos");
      // espera 5 segundos antes de reintentar
      delay(5000);
    }
    retries--;
    if (retries == 0) {
      // esperar a que el WDT lo reinicie
      while (1);
    }
  }
}

//------------------------SETUP-----------------------------
void setup() {

  //prepara GPI13 y 12 como salidas 
  pinMode(13, OUTPUT); // D7 salida analógica
  analogWrite(13, 0); // analogWrite(pin, value);
  pinMode(12, OUTPUT); // D6 salida digital
  digitalWrite(12, LOW);

  // Entradas
  pinMode(14, INPUT); // D5

  // Inicia Serial
  Serial.begin(115200);
  Serial.println("");

  // Conexión WIFI
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED and contconexion <50) { //Cuenta hasta 50 si no se puede conectar lo cancela
    ++contconexion;
    delay(500);
    Serial.print(".");
  }
  if (contconexion <50) {
      //para usar con ip fija
      IPAddress ip(192,168,1,156); 
      IPAddress gateway(192,168,1,1); 
      IPAddress subnet(255,255,255,0); 
      WiFi.config(ip, gateway, subnet); 
      
      Serial.println("");
      Serial.println("WiFi conectado");
      Serial.println(WiFi.localIP());
  }
  else { 
      Serial.println("");
      Serial.println("Error de conexion");
  }
  
  client.setServer(SERVER, SERVERPORT);
  client.setCallback(callback);

  String temperatura = "/" + USERNAME + "/" + "temperatura"; 
  temperatura.toCharArray(TEMPERATURA, 50);
  String pulsador = "/" + USERNAME + "/" + "pulsador"; 
  pulsador.toCharArray(PULSADOR, 50);
  String salidaDigital = "/" + USERNAME + "/" + "salidaDigital"; 
  salidaDigital.toCharArray(SALIDADIGITAL, 50);
  String salidaAnalogica = "/" + USERNAME + "/" + "salidaAnalogica"; 
  salidaAnalogica.toCharArray(SALIDAANALOGICA, 50);
  
}

//--------------------------LOOP--------------------------------
void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  unsigned long currentMillis = millis();
    
  if (currentMillis - previousMillis >= 10000) { //envia la temperatura cada 10 segundos
    previousMillis = currentMillis;
    int analog = analogRead(17);
    float temp = analog*0.322265625;
    strtemp = String(temp, 1); //1 decimal
    strtemp.toCharArray(valueStr, 15);
    Serial.println("Enviando: [" +  String(TEMPERATURA) + "] " + strtemp);
    client.publish(TEMPERATURA, valueStr);
  }
  
  if (digitalRead(14) == 0) {
    strPulsador = "presionado";
  } else {
    strPulsador = "NO presionado";
  }

  if (strPulsador != strPulsadorUltimo) { //envia el estado del pulsador solamente cuando cambia.
    strPulsadorUltimo = strPulsador;
    strPulsador.toCharArray(valueStr, 15);
    Serial.println("Enviando: [" +  String(PULSADOR) + "] " + strPulsador);
    client.publish(PULSADOR, valueStr);
  }
}

27 comentarios:

  1. Estimado,

    Al buscar la librería esp8266wifi no aparece en la opción del gestor de librerías del IDE. La he buscado y no la encuentro en github. Estoy usando la versión 1.8.5 de arduino. ¿Dónde podría encontrar esa librería o cuál sería la otra forma de enviar datos a través de ese dispositivo wifi?

    Saludos

    ResponderEliminar
    Respuestas
    1. Mira acate dejo un tutorial de como instalar las librerias:

      https://programarfacil.com/esp8266/como-programar-nodemcu-ide-arduino/

      Eliminar
  2. Hola,

    Gracias por el tutorial, útil para entender como funciona cloudmqtt.
    Funciona practicamente todo... pero no consigo desde el websocket UI de cloudmqtt ver las lecturas y forzar salidas en la placa sino defino un topic como: placa1 - #
    Desconozco si es porque cloudmqtt ha hecho algunos cambios desde que publicaste el vídeo. ¿Sabes como lo debería hacer sin necesidad de utilizar esta wildcard?

    Gracias,

    ResponderEliminar
    Respuestas
    1. Ya lo he resuelto yo mismo... en topic hay que poner toda la cadena

      Eliminar
    2. yo tengo ese problema podrias ayudarme a que te refieres con toda la cadena

      Eliminar
    3. Tengo el mismo problema, a que te refieres con toda la cadena?

      Eliminar
    4. Hola Pepe me podrás ayudar tengo este problema no encuentro como resolverlo Muchas Gracias mi correo lealadrianar@yahoo.es

      Eliminar
  3. Buenas a todos....son mis primeros pasos con mqtt y tengo el mismo problema con websocket UI no puedo visualizar los msj

    ResponderEliminar
  4. Buenas Alejandro, antes de nada muchísimas gracias por todo el material que tiene publicado, es de gran ayuda.
    Tengo un problema que no consigo solventar, no se si es porque no tengo bien hecha la función.
    Lo que quiero hacer es que si la alarma se activa EstadoAlarma1 (entrada booleana) y le mando un 0 por mqtt, la alarma se apague, la cosa es que cuando está activa y le mando un 0 esta sigue igual. Le dejo la función por si acaso la estoy haciendo mal:

    void callback(char* topic, byte* payload, unsigned int length) {
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    }
    Serial.println();

    if (EstadoAlarma1==true && (char)payload[0] == '0') {
    digitalWrite(pinLED, LOW);
    Serial.println("Alarma desactivada");
    client.publish("dev/test", "ALARMA 1 DESACTIVADA");
    EstadoAlarma1=false;
    } else{
    }
    }

    Muchísimas gracias!

    ResponderEliminar
  5. Pueden ser mucho errore. ¿en algún lugar del código pone EstadoAlarma1==true?

    ResponderEliminar
  6. Hola amigo muy buenas y gracias de antemano por tus enseñanzas.
    Disculpa tengo un problema al realizar la conección ya que no se conecta e intentado varios procedimientos pero nada
    En mi caso no me sale m11.cloudmqtt.com sino m15.cloudmqtt.com. Ya puse la ip de aquella y la misma m15.cloudmqtt.com pero no se conecta.
    Agradecería tu ayuda.

    ResponderEliminar
    Respuestas
    1. Con la información que me das se me hace imposible ayudarte. Te recomendaría que veas el tutorial 12 para conectarte con el smartphone antes de intentar conectarte con el ESP. Saludos.
      https://www.youtube.com/watch?v=MuTaFhuMdAo

      Eliminar
  7. hola amigo , muchas gracias por tus aportes desde que vi este video estuve buscando mucha inforacion de como hacer mi propio broker de mqtt como el servicio que usas de broker y termine aprendiendo nodejs , ahorita mismo acabo de terminar mis puerbas sobre mi mqtt broker y todo funciona como esperaba y todo gracias a tus videos! me que me inspiraron y me dieron muchas ideas !

    ResponderEliminar
  8. gracias por el aporte! saludos desde Guanajuato México

    ResponderEliminar
  9. Hola amigo, tengo un problema al enviar los datos de un sensor de gas mq135, resulta que la conexion anda bien. pero al momento de enviar los datos a cloudmqtt no los envia. especificamente en client.publish():

    ResponderEliminar
  10. Con la información qu eme das es imposible saber donde puede estar el problema. Saludos.

    ResponderEliminar
  11. Hola Alejandro, siguiendo tu consejo vi todo tus videos sobre MQTT para poder controlar temperatura via un dominio externo, luego comencé a seguir tus pasos, primer problema la pagina de cloudmqtt a cambiado y no puedos poner los topic, si se puede crear instancias.
    Gracias por lo mucho que nos enseñas, espero algun dia conocerte

    ResponderEliminar
  12. Hola alejandro, tenia una pregunta si has trabajado CloudMQTT con MIT app Inventor, es a ver si me puedes dar un consejo sobre como conectar una aplicacion propia creada con MITapp conectada con CoudMQTT y poder controlar varias placas

    ResponderEliminar
  13. HOLA ALEJANDRO AGRADESCO TU TUTORIAL
    TENGO UN PROBLEMA CUANDO MONTO EL CODIGO
    LO MONTO IGUAL REALIZANDO LOS CAMBIOS QUE SON Y ME SALE UNA FALLA


    Attempting MQTT connection...failed, rc=5 try again in 5 seconds

    DESEARIA SABER SI SABES COMO SOLUCIONARLA YA QUE NO E PODIDO ENCONTRAR EL ERROR MUCHAS GRACIAS

    ResponderEliminar
    Respuestas
    1. igual tengo el mismo error no logro solucionarlo tu ya ?

      Eliminar
    2. char SERVER[50] = "3.83.223.148"; // soldier.cloudmqtt.com
      int SERVERPORT = 17458; CHECA EL LA NUMERACION DEL PORT QUE TE DA LA PAGINA AL IGUAL QUE EL SERVER
      String USERNAME = "placa1";
      char PASSWORD[50] = "12345678";

      Eliminar
    3. Hola, probé el código, no me funcionó, tuve errores como "rc=-2" ó "rc=5".
      Metiendo mano y buscando en distintos foros y canales de Youtube, en ellos encontré que esos problemas eran porque no había conexión. ¿Pero como?, si hay un bucle que me indica si se conectó o no, bueno, lo solucione comentando estas líneas:


      void setup() {
      // Inicia Serial
      Serial.begin(115200);
      Serial.println("");


      //prepara GPI13 y 12 como salidas
      pinMode(13, OUTPUT); // D7 salida analógica
      analogWrite(13, 0); // analogWrite(pin, value);
      pinMode(12, OUTPUT); // D6 salida digital
      digitalWrite(12, LOW);

      // Entradas
      pinMode(14, INPUT); // D5


      // Conexión WIFI
      WiFi.mode(WIFI_STA);
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED and contconexion <50) { //Cuenta hasta 50 si no se puede conectar lo cancela
      // ++contconexion;
      delay(500);
      Serial.print(".");
      }/*
      if (contconexion <50) {
      //para usar con ip fija
      IPAddress ip(192,168,1,156);
      IPAddress gateway(192,168,1,1);
      IPAddress subnet(255,255,255,0);
      WiFi.config(ip, gateway, subnet);

      Serial.println("");
      Serial.println("WiFi conectado");
      Serial.println(WiFi.localIP());
      }
      else {
      Serial.println("");
      Serial.println("Error de conexion");
      }*/

      client.setServer(SERVER, SERVERPORT);
      client.setCallback(callback);

      String temperatura = "/" + USERNAME + "/" + "temperatura";
      temperatura.toCharArray(TEMPERATURA, 50);
      String pulsador = "/" + USERNAME + "/" + "pulsador";
      pulsador.toCharArray(PULSADOR, 50);
      String salidaDigital = "/" + USERNAME + "/" + "salidaDigital";
      salidaDigital.toCharArray(SALIDADIGITAL, 50);
      String salidaAnalogica = "/" + USERNAME + "/" + "salidaAnalogica";
      salidaAnalogica.toCharArray(SALIDAANALOGICA, 50);

      }

      Espero haberlos ayudado.

      Gracias Alejandro por compartir la información!! Sos un genio <3!!

      Eliminar
  14. Hola Soy Adrian de Mendoza estoy tratando de hacer una conexion y no lo consigo!! no se conecta con el CloudMqtt. siempre esta intentando y no lo hace me sale que la falla es rc=2
    ya he probado poniendo los topics de muchas formas y nada
    Pregunto tengo que Habilitar en mi router algun puerto de los que me asigna CloudMqTT?? o que otra cosa puedo hacer agradeseria mucho la ayuda!! Saludos Gracias

    ResponderEliminar
  15. ..Intentando conexion MQTT...fallo, rc=-4 intenta nuevamente en 5 segundos ayuda porfavor....

    ResponderEliminar
    Respuestas
    1. hola lo hice funcionar
      1º fijate de comentar las 4 lineas debajo de:
      //para usar con ip fija
      luego fijate de cargar el pattern; por ejemplo:
      usuario - temperatura tanto en la placa ESP8266 como en el CloudMQTT..

      Eliminar
  16. Hola Alejandro, muy interesantes tus vídeos. A ver si tú me puedes ayudar.
    Tengo un problema al que no consigo encontrar solución y eso que he mirado ampliamente por internet.
    Estoy intentando introducir el protocolo MQTT en un proyecto que ya tenía funcionando por web server pero que lógicamente adolecía de una latencia elevada. Tengo un script en python que detecta el estado de unos sensores y genera un 'publish' por cada sensor. El servido MQTT es mosquitto alojado en una raspberry. Con diferentes aplicaciones externas visualizo correctamente la publicación de los topics pero mi aplicación de Esp8266, la callback, que básicamente es un calco de las que corren por internet, siempre detecta la misma longitud: 1. Da igual la información que envíe. Sin embargo los diferentes clientes de MQTT que tengo detectan correctamente la cadena que envío.
    Qué está pasando? Qué hago mal?


    void setup()    {
        .....
        
        char* format = "manso/sns%d";
        sprintf(mqtt_topic_sns, format, SENSOR);

        mqttClient.setCallback(receivedMessage);
        if (connect()) {
        #if DEBUG
            Serial.print("Conectado a MQTT como ");Serial.println(clientID);
            Serial.print("En el tópico ");Serial.println(mqtt_topic_sns);
        #endif
        } else {
            #if DEBUG
            Serial.println("Connection Failed!");
            #endif
        }
    }

    void loop() {    
    if (!mqttClient.connected()) {
        connect();
    }
    mqttClient.loop();

        ....
        
    }

    void receivedMessage(char* topic, byte* payload, unsigned int length) {
        String cargo = "";
        for (int i = 0; i < length; i++) {
            cargo += (char)payload[i];
        }
        
        #if DEBUG
        Serial.print("Carácter recibido: ");Serial.println((char)payload[0]);
        Serial.print("topic: ");Serial.println(topic);
        Serial.print("mqtt_topic_sns: ");Serial.println(mqtt_topic_sns);
        Serial.print("cargo: ");Serial.println(cargo);
        Serial.print("LENGTH: ");Serial.println(length);
        #endif

        if(strcmp(topic, mqtt_topic_sns) == 0) {
            #if DEBUG
            Serial.print("Envío a mqttUps: ");Serial.println(cargo);
            #endif
            mqttUps(String(cargo));
        }
    }


    ResponderEliminar
  17. Hola, que tal. Intente realizar éste ejercicio y lo pude hacer con la clave y usuario principal (la que se crea con la instancia) pero no con el usuario que creo para una placa (ejemplo Placa1) en particular. Alguna idea de donde puede estar el inconveniente? Gracias

    ResponderEliminar