Mit MQTT einen Roboter steuern - [Teil 2] - AZ-Delivery

En la primera parte de esta serie de blogs aprendiste todo lo que necesitas saber sobre MQTT. Además, se configuró un servidor, el llamado broker, en una Raspberry Pi y se utilizaron entradas de línea de comandos para enviar y también recibir mensajes al broker.

En esta parte, la configuración se vuelve un poco más compleja, con datos enviados y recibidos por varios NodeMCUs y un Arduino Uno con Ethernet Shield, llamados clientes.

El fondo será que se puede ejecutar un cliente MQTT en los microcontroladores más comunes que vende AZ-Delivery. En los requisitos de hardware puedes leer qué partes son obligatorias para completar la serie. Algunos componentes sólo son necesarios para esta parte del blog, pero se utilizan en varias otras entradas del blog de AZ-Delivery.

Requisitos del hardware

Solo necesita algunos componentes para esta publicación de blog, consulte tabla 1.

número Componente
1 Raspberry Pi (requerido)
1 Fuente de alimentación a juego
1 Módulo NodeMCU Lua Amica V2 ESP8266 ESP-12F(necesario)
1 Placa de desarrollo WiFi ESP32 NodeMCU Module WLAN (necesario)
1 Placa de microcontrolador con cable USB (Opcional)
1 Escudo Ethernet W5100 (Opcional)
1 Gama de resistencias (Opcional)
2 Potenciómetro (necesario)
1 Pantalla LCD 4x20 caracteres azul I2C (Opcional)
1 LED de un solo color (Opcional)

Tabla 1: Hardware necesario

Con el Pi, tenga en cuenta que además del hardware mencionado anteriormente, también necesitará una tarjeta microSD. Para hacer esto, debe instalar el sistema operativo Raspberry Pi (anteriormente conocido como Raspbian) como una imagen en la tarjeta.

Requisito de software

El software requerido para este proyecto es manejable:

  • IDE de Arduino (https://www.arduino.cc/en/Main/Software), es mejor descargar la versión actual aquí
  • La biblioteca PubSubClient con todas las dependencias
  • La biblioteca LiquidCrystal_I2C con todas las dependencias

Cómo puede instalar bibliotecas a través de la gestión de bibliotecas se encuentra en https://www.az-delivery.de/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/arduino-ide-programmieren-fuer-einsteiger-teil-1 Sección Gestión de bibliotecas, descrita con más detalle.

Requisito básico

Se requiere una Raspberry Pi con un agente MQTT instalado para que esta y las siguientes publicaciones del blog funcionen. Puede leer exactamente cómo funciona esto y qué comandos debe ingresar en la Parte 1 de esta serie de blogs. Compruebe también si el corredor también se inició cuando se inició Raspberry Pi. Para hacer esto, emita el comando Codigo 1 en la terminal.

sudo service mosquitto status

Código 1: consulta en la terminal si mosquitto ha comenzado

La salida debe ser un activo (corriendo) mostrar ver ilustración 1, no se necesitan más comandos.

Figura 1: Estado del broker mosquitto en la terminal

Si el resultado es diferente, verifique nuevamente si mosquitto se ha instalado correctamente y el servicio también se ha integrado correctamente en el inicio automático. Las instrucciones exactas se pueden encontrar en la Parte 1.

Suscripción simple a MQTT con el módulo NodeMCU Lua Amica V2 ESP8266

Una nota en este punto: El módulo NodeMCU Lua Amica V2 ESP8266 se utiliza para el ejemplo descrito aquí.

En el primer ejemplo, vea Codigo 2, NodeMCU debe establecer una conexión con el intermediario MQTT y recibir todos los datos. Esto también le da una idea directa de cómo funciona la comunicación general con la biblioteca PubSubClient. Cada función se ha descrito en detalle para que pueda comprender mejor el código fuente.

Para que el código fuente funcione con usted, debe cambiar su ID de WLAN y contraseña en el código fuente.


//-----------------------------------------------------
// Ejemplo 1 NodeMCU con MQTT
// Autor: Joern Weise
// Licencia: GNU GPl 3.0
// Creado: 20 de octubre de 2020
// Actualización: 25 de octubre de 2020
//-----------------------------------------------------
#include // Lib para Wifi
#include // Lib para MQTT Pub y Sub
//
#ifndef STASSID
#define STASSID "................"
#define STAPSK "................"
#endif

#define ADVANCEDIAG 1
const char * MQTT_BROKER = "raspberrypi"; // Nombre del corredor mqtt
const char * SUBTOPIC = "/ #";
String clientID = "NodeMCU_1"; // Nombre de cliente para MQTT-Broker
WiFiClient espClient;
PubSubClient mqttClient (espClient);

configuración vacía ()
{
Serial.begin (115200); // Iniciar la velocidad en baudios del monitor serial 115200
retraso (50);
writeAdvanceDiag ("SerialMonitor habilitado", verdadero);
setupWifi ();
writeAdvanceDiag ("Establecer servidor MQTT", verdadero);
mqttClient.setServer (MQTT_BROKER, 1883);
writeAdvanceDiag ("Establecer función de devolución de llamada", verdadero);
mqttClient.setCallback (devolución de llamada);
writeAdvanceDiag ("Finalizar configuración () - Función", verdadero);
}

/*
* =================================================================
* Función: setupWifi
* Devoluciones: nulo
* Descripción: Configurar wifi para conectarse a la red
* =================================================================
*/
configuración vacía Wi-Fi ()
{
Serial.println ("Conexión a:" + Cadena (STASSID));
Modo WiFi (WIFI_STA);
WiFi.begin (STASSID, STAPSK);
mientras (WiFi.status ()! = WL_CONNECTED)
  {
retraso (500);
Serial.print (".");
  }
Serial.println ("");
Serial.println ("WiFi conectado");
Serial.println ("dirección IP:");
Serial.println (WiFi.localIP ());
}

bucle vacío () {
// ponga su código principal aquí, para que se ejecute repetidamente:
si (! mqttClient.connected ())
reconnectMQTT ();

mqttClient.loop ();
}

/*
* =================================================================
* Función: devolución de llamada
* Devoluciones: nulo
* Descripción: se llamará automáticamente, si un tema suscrito
* tiene un mensaje nuevo
* tema: devuelve el tema, de donde proviene un nuevo mensaje
* payload: el mensaje del tema
* longitud: longitud del mensaje, importante para obtener contenido
* =================================================================
*/
devolución de llamada void (char * topic, byte * payload, unsigned int length)
{
String stMessage = "";
writeAdvanceDiag ("Mensaje recibido del tema:" + Cadena (tema), verdadero);
writeAdvanceDiag ("Longitud del mensaje:" + Cadena (longitud), verdadero);
para (int i = 0; i <longitud; i ++)
stMessage + = String ((char) payload [i]);
writeAdvanceDiag ("El mensaje es:" + stMessage, verdadero);
}

/*
* =================================================================
* Función: reconnectMQTT
* Devoluciones: nulo
* Descripción: Si no hay conexión a MQTT, esta función es
* llamado. Además, se registra el tema deseado.
* =================================================================
*/
anular reconectar MQTT ()
{
while (! mqttClient.connected ())
  {
writeAdvanceDiag ("Iniciar sesión en MQTT-Broker", verdadero);
si (mqttClient.connect (clientID.c_str ()))
    {
Serial.println ("Conectado a MQTT-Broker" + Cadena (MQTT_BROKER));
writeAdvanceDiag ("Suscribir tema '" + Cadena (SUBTOPIC) + "'", verdadero);
mqttClient.subscribe (SUBTOPIC, 1); // Subtema "SUBTOPIC"
    }
más
    {
writeAdvanceDiag ("Falló con rc =" + String (mqttClient.state ()), true);
Serial.println ("Siguiente MQTT-Connect en 3 segundos");
retraso (3000);
    }
  }
}

/*
* =================================================================
* Función: writeAdvanceDiag
* Devoluciones: nulo
* Descripción: escribe msg avanzado en el monitor serial, si
* ADVANCEDIAG> = 1
* msg: Mensaje para el monitor serial
* newLine: Mensaje con salto de línea (verdadero)
* =================================================================
*/
void writeAdvanceDiag (String msg, bool newLine)
{
if (bool (ADVANCEDIAG)) // Compruebe si el diagnóstico avanzado está habilitado
  {
si (newLine)
Serial.println (msg);
más
Serial.print (msg);
  }
}


Código 2: Ejemplo NodeMCU Recibir datos MQTT


Al principio del código, se crea un objeto espClient y se le pasa al también creado objeto mqttClient. Esto permite establecer una comunicación general cuando hay una conexión WiFi activa.

Cuando el NodeMCU se inicia, primero se configura el WiFi aquí en la función setupWifi () y luego se establece la conexión con el broker MQTT en la función setup (). Con "mqttClient.setServer (MQTT_BROKER, 1883);" se configura la dirección y el puerto del broker MQTT y con "mqttClient.setCallback (callback);" se determina la función para procesar los datos recibidos, más sobre esto a continuación.

La función loop () hace exactamente dos cosas:

1. Comprueba si el NodeMCU está conectado al broker MQTT. Si no es así, se llama a la función reconnectMQTT ().

2. Se llama a la función mqttClient.loop () para realizar la comunicación con MQTT.

El primer paso es registrarse con el corredor de MQTT con un nombre de cliente único. Puede definirlo libremente utilizando la variable global "clientID". Si el registro fue exitoso, el tema deseado se suscribe en el siguiente paso. Esto se hace usando

"MqttClient.subscribe (SUBTOPIC, 1)"

donde en este ejemplo SUBTOPIC se suscribe al tema “/ #” (todos los temas) y “1” representa una QoS mayor que 0. Por lo tanto, el NodeMCU recibe mensajes del intermediario tan pronto como hay cambios en un tema.

En caso de error, NodeMCU intenta volver a conectarse al corredor cada 3 segundos.

Esto nos lleva al punto en el que NodeMCU recibe datos del corredor si el NodeMCU está conectado. Si hay mensajes nuevos, estos se evalúan mediante la función callback (). Se pasan tres parámetros a la función callback ():

  1. tema: que refleja la ruta absoluta del tema del mensaje recibido
  2. payload: el mensaje, en formato de bytes
  3. longitud: la longitud del mensaje

Con estos tres datos, puede evaluar para qué necesita el tema seleccionado. En el caso presente, generamos el tema a través del monitor en serie. Inmediatamente después, convertimos el mensaje de bytes en una cadena usando el bucle for que se muestra en el Código 3. Tiene un mensaje que se puede leer normalmente.

para (int i = 0; i <longitud; i ++)
stMessage + = String ((char) payload [i]);

Código 3: fragmento de código para convertir el mensaje MQTT en una cadena

Este mensaje en formato de cadena también se emite a través del monitor en serie

Puede ver en la Figura 2 que este boceto funciona tan bien.

Figura 2: Mensaje recibido del corredor de MQTT

Por favor considere antes de sus mensajes que nuestras diéresis alemanas no funcionan. Estos luego son representados por otros personajes.

Suscripción simple a MQTT con ESP32 NodeMCU

Tenga en cuenta que en este punto, la placa de desarrollo WLAN WiFi del módulo ESP32 NodeMCU se utiliza para el siguiente ejemplo.

En el ejemplo del Código 4, el ESP32 NodeMCU debe enviar datos al corredor MQTT. Dado que es muy simple, comenzamos enviando el tiempo de ejecución desde la placa de desarrollo WLAN WiFi del módulo ESP32 NodeMCU al corredor cada dos segundos.

También en este ejemplo, hay muchos comentarios en el código para que sea más fácil de entender. Para que el código fuente funcione con usted, debe cambiar su ID de WLAN y contraseña en el código fuente.


//-----------------------------------------------------
// Ejemplo 2 ESP-NodeMCU con MQTT
// Autor: Joern Weise
// Licencia: GNU GPl 3.0
// Creado: 20 de octubre de 2020
// Actualización: 25 de octubre de 2020
//-----------------------------------------------------
#include
#include // Lib para MQTT Pub y Sub
//
#ifndef STASSID
#define STASSID "……………" // Ingrese el nombre Wfi
#define STAPSK "……………" // Ingrese la contraseña
#endif

#define ADVANCEDIAG 1
const char * MQTT_BROKER = "raspberrypi"; // Nombre del corredor mqtt
const char * PubTopic = "/ Client / ESP32"; // Tema donde publicar
String clientID = "ESP-DevKit_1"; // Nombre del cliente para el intermediario MQTT
WiFiClient espClient;
PubSubClient mqttClient (espClient);

unsigned long lLastMsg = 0;
int iTimeDelay = 2000; // Establecer la demora para el siguiente mensaje en 2 segundos
#define MSG_BUFFER_SIZE (50)
char msg [MSG_BUFFER_SIZE];

configuración vacía ()
{
Serial.begin (115200); // Iniciar la velocidad en baudios del monitor en serie 115200
retraso (50);
writeAdvanceDiag ("SerialMonitor habilitado", verdadero);
setupWifi ();
writeAdvanceDiag ("Establecer servidor MQTT", verdadero);
mqttClient.setServer (MQTT_BROKER, 1883);
writeAdvanceDiag ("Finalizar configuración () - Función", verdadero);
}

/*
* =================================================================
* Función: setupWifi
* Devoluciones: nulo
* Descripción: Configurar wifi para conectarse a la red
* =================================================================
*/
configuración vacía Wi-Fi ()
{
Serial.println ("Conexión a:" + Cadena (STASSID));
Modo WiFi (WIFI_STA);
WiFi.begin (STASSID, STAPSK);
mientras (WiFi.status ()! = WL_CONNECTED)
  {
retraso (500);
Serial.print (".");
  }
Serial.println ("");
Serial.println ("WiFi conectado");
Serial.println ("dirección IP:");
Serial.println (WiFi.localIP ());
}

bucle vacío () {
// ponga su código principal aquí, para que se ejecute repetidamente:
si (! mqttClient.connected ())
reconnectMQTT ();

mqttClient.loop ();

if (millis () - lLastMsg> iTimeDelay)
  {
lLastMsg = millis ();
snprintf (msg, MSG_BUFFER_SIZE, "% 1d", millis ()); // Convertir mensaje a char
mqttClient.publish (PubTopic, msg, true); // Enviar al corredor
  }
}

/*
* =================================================================
* Función: reconnectMQTT
* Devoluciones: nulo
* Descripción: Si no hay conexión a MQTT, esta función es
* llamado. Además, se registra el tema deseado.
* =================================================================
*/
anular reconectar MQTT ()
{
while (! mqttClient.connected ())
  {
writeAdvanceDiag ("Iniciar sesión en MQTT-Broker", verdadero);
si (mqttClient.connect (clientID.c_str ()))
    {
Serial.println ("Conectado a MQTT-Broker" + Cadena (MQTT_BROKER));
    }
más
    {
writeAdvanceDiag ("Falló con rc =" + String (mqttClient.state ()), true);
Serial.println ("Siguiente MQTT-Connect en 3 segundos");
retraso (3000);
    }
  }
}

/*
* =================================================================
* Función: writeAdvanceDiag
* Devoluciones: nulo
* Descripción: escribe msg avanzado en el monitor serial, si
* ADVANCEDIAG> = 1
* msg: Mensaje para el monitor serial
* newLine: Mensaje con salto de línea (verdadero)
* =================================================================
*/
void writeAdvanceDiag (String msg, bool newLine)
{
if (bool (ADVANCEDIAG)) // Compruebe si el diagnóstico avanzado está habilitado
  {
si (newLine)
Serial.println (msg);
más
Serial.print (msg);
  }
}

Código 4: ESP32 MQTT-Daten empfangen lassen

Al igual que en el primer ejemplo, se crea un objeto espClient y se le pasa al objeto igualmente creado mqttClient. Cuando se inicia el ESP32 Módulo NodeMCU WLAN Placa de desarrollo WiFi, se configura el WiFi mediante la función setupWiFi (). Directamente después de la función setup () - se preconfigura el broker MQTT mediante "mqttClient.setServer (MQTT_BROKER, 1883);". Una función de devolución de llamada () - no es necesaria en este punto, porque no hay datos que deban ser recibidos y procesados.

Por lo tanto, la función de bucle () - es casi idéntica a la del primer ejemplo, con la excepción de la línea que muestra el Código 5.


if (millis () - lLastMsg> iTimeDelay)
{
lLastMsg = millis ();
snprintf (msg, MSG_BUFFER_SIZE, "% 1d", millis ()); // Convertir mensaje a char
mqttClient.publish (PubTopic, msg, true); // Enviar al corredor
}

Código 5: Fragmento de código para enviar el tiempo activo

Esta parte envía el tiempo de ejecución actual al tema "/ Cliente / ESP32" cada 2 segundos. La figura 3 muestra que este ejemplo funciona. En este caso, MQTT.fx muestra los mensajes del broker MQTT.

Abbildung 3: MQTTfx zeigt gesendete Daten vom ESP32 NodeMCU

Ejemplo de clientes MQTT con múltiples


En los últimos ejemplos, incluida la primera entrada del blog, siempre hemos supuesto un corredor y un cliente. Sin embargo, la realidad de estos proyectos MQTT suele ser diferente. En la práctica, suele haber clientes que entregan los datos de los sensores, clientes que reciben y procesan los datos empujados y, por último, clientes que ejecutan.

Para el siguiente ejemplo, un Módulo Amica V2 de NodeMCU Lua monitorizará el valor de un potenciómetro y enviará el nuevo valor al broker cuando cambie.

Un módulo ESP32 NodeMCU WLAN WiFi Development Board suscribe estos datos y los mapea de 0-1024 a 0-255, además se publica desde dos sensores BME / BMP280 la temperatura y la presión atmosférica.

Estos datos recogidos se suscriben en Arduino Uno con Ethernet-Shield, LED y I2C-LCD-Display y se visualizan los datos. El LED proporciona la señal mapeada del potenciómetro y la pantalla muestra la temperatura y la presión atmosférica de los dos sensores.

Comenzamos con el módulo NodeMCU Lua Amica V2 y el potenciómetro. Conecte ambos cables como se muestra en la Figura 4. Esta parte es fácil ya que solo hay que conectar tres cables.

 

Figura 4: Cableado del módulo NodeMCU Lua Amica V2 con potenciómetro

El código para el módulo NodeMCU Lua Amica V2 se mantiene delgado y esencialmente incluye la inicialización de la WLAN y la conexión a MQTT, así como la transmisión de datos de los valores del potenciómetro analógico, ver Código 6. Para que el código fuente también se ejecute en usted, debe usar su WLAN Change ID y contraseña en el código fuente.

//-----------------------------------------------------
// Ejemplo 3 NodeMCU con transferencia Poti a mqtt-broker
// Autor: Joern Weise
// Licencia: GNU GPl 3.0
// Creado: 20 de octubre de 2020
// Actualización: 25 de octubre de 2020
//-----------------------------------------------------
#include // Lib para Wifi
#include // Lib para MQTT Pub y Sub

#ifndef STASSID
#define STASSID "ENTER-WIFI_HERE" // Ingrese Wfi-Name
#define STAPSK "ENTER-PASS-HERE" // Ingrese Wifi-Passkey
#endif
#define ADVANCEDIAG 1
#define MSG_BUFFER_SIZE (50)
#define UPDATETIME 200
#define MINVALUECHANGE 1

const char * MQTT_BROKER = "raspberrypi"; // Nombre del corredor mqtt
String clientID = "NodeMCU_1"; // Nombre del cliente para el intermediario MQTT
const char * PubTopicPoti = "/ Cliente / ESP32 / Poti / Value"; // Tema donde publicar
const int iAnalogPin = A0; // Establecer pin analógico
int iSensorValue = 0;
int iLastValue = 0;

// Crea objetos para mqtt
WiFiClient espClient;
PubSubClient mqttClient (espClient);
unsigned int iLastTime = 0;
char msg [MSG_BUFFER_SIZE];
configuración vacía ()
{
Serial.begin (115200); // Iniciar la velocidad en baudios del monitor en serie 115200
retraso (50);
writeAdvanceDiag ("SerialMonitor habilitado", verdadero);
setupWifi ();
writeAdvanceDiag ("Establecer servidor MQTT", verdadero);
mqttClient.setServer (MQTT_BROKER, 1883);
writeAdvanceDiag ("Establecer función de devolución de llamada", verdadero);
writeAdvanceDiag ("Finalizar configuración () - Función", verdadero);
}

/*
=================================================================
Función: setupWifi
Devoluciones: nulo
Descripción: Configurar wifi para conectarse a la red
=================================================================
*/
configuración vacía Wi-Fi ()
{
Serial.println ("Conexión a:" + Cadena (STASSID));
Modo WiFi (WIFI_STA);
WiFi.begin (STASSID, STAPSK);
mientras (WiFi.status ()! = WL_CONNECTED)
  {
retraso (500);
Serial.print (".");
  }
Serial.println ("");
Serial.println ("WiFi conectado");
Serial.println ("dirección IP:");
Serial.println (WiFi.localIP ());
}

bucle vacío () {
// ponga su código principal aquí, para que se ejecute repetidamente:
si (! mqttClient.connected ())
reconnectMQTT ();

mqttClient.loop ();

if (millis () - iLastTime> UPDATETIME)
  {
iSensorValue = analogRead (iAnalogPin); // Leer valor analógico
si (iSensorValue! = iLastValue)
    {
if (abs (iSensorValue - iLastValue)> MINVALUECHANGE) // Verifica si el cambio es lo suficientemente alto
      {
Serial.println ("Sensorvalue:" + String (iSensorValue));
snprintf (msg, MSG_BUFFER_SIZE, "% d", iSensorValue); // Convertir mensaje a char
mqttClient.publish (PubTopicPoti, msg); // Enviar al corredor
iLastValue = iSensorValue;
      }
    }
iLastTime = milis ();
  }
}

/*
=================================================================
Función: reconnectMQTT
Devoluciones: nulo
Descripción: Si no hay conexión con MQTT, esta función es
llamado. Además, se registra el tema deseado.
=================================================================
*/
anular reconectar MQTT ()
{
while (! mqttClient.connected ())
  {
writeAdvanceDiag ("Iniciar sesión en el intermediario MQTT", verdadero);
si (mqttClient.connect (clientID.c_str ()))
    {
Serial.println ("Conectado a MQTT Broker" + String (MQTT_BROKER));
    }
más
    {
writeAdvanceDiag ("Falló con rc =" + String (mqttClient.state ()), true);
Serial.println ("Siguiente MQTT-Connect en 3 segundos");
retraso (3000);
    }
  }
}

/*
=================================================================
Función: writeAdvanceDiag
Devoluciones: nulo
Descripción: Escribe el mensaje de avance en el monitor serie, si
ADVANCEDIAG> = 1
msg: Mensaje para el monitor serial
newLine: Mensaje con salto de línea (verdadero)
=================================================================
*/
void writeAdvanceDiag (String msg, bool newLine)
{
if (bool (ADVANCEDIAG)) // Compruebe si el diagnóstico avanzado está habilitado
  {
si (newLine)
Serial.println (msg);
más
Serial.print (msg);
  }
}

Código 6: Módulo NodeMCU Lua Amica V2

Para ayudarlo a comprender el código fuente más rápidamente, se han anotado muchos comentarios y notas sobre las funciones.

El próximo microcontrolador que se configurará y se le asignará su función aquí es la placa de desarrollo WLAN WiFi del módulo ESP32 NodeMCU. Como tiene dos interfaces I2C, recibe dos sensores BME / BMP280 y asume el mapeo de los valores del potenciómetro analógico. Nuevamente, el cableado es simple, vea la Figura 5.

Figura 5: Cableado de la placa de desarrollo WLAN WiFi del módulo ESP32 NodeMCU

El código para estas tareas, consulte Código 7, es un poco más extenso. Primero, se inicializan la conexión WLAN, la conexión MQTT y los sensores. En la función de devolución de llamada, el valor del potenciómetro analógico recién recibido se asigna a un valor entre 0 y 255 y se envía de vuelta al corredor. Si uno o ambos BME / BMP280 tienen nuevos datos de medición que se desvían de los valores anteriores, estos también se transmiten al corredor.

Para que el código fuente funcione con usted, debe cambiar su ID de WLAN y contraseña en el código fuente.

//-----------------------------------------------------
// Ejemplo 3 ESP-NodeMCU con dos transferencias BME a
// mqtt broker y mapeo de entrada analógica
// Autor: Joern Weise
// Licencia: GNU GPl 3.0
// Creado: 20 de octubre de 2020
// Actualización: 25 de octubre de 2020
//-----------------------------------------------------
#include
#include
#include
#include // Lib para MQTT Pub y Sub

// Definir la configuración de WiFi
#ifndef STASSID
#define STASSID "ENTER-WIFI-HERE" // Ingrese Wfi-Name
#define STAPSK "ENTER-PASS-HERE" // Ingrese la contraseña
#endif

#define ADVANCEDIAG 1

#define I2C_SDA1 21
#define I2C_SCL1 22
#define I2C_SDA2 17
#define I2C_SCL2 16
#define NEXTUPDATE 2000

// Objetos para I2C y BME
TwoWire I2Cone = TwoWire (0);
TwoWire I2Ctwo = TwoWire (1);
Adafruit_BME280 bmeOne;
Adafruit_BME280 bmeTwo;
unsigned long lastTime = 0;

const char * MQTT_BROKER = "raspberrypi"; // Nombre del corredor mqtt
const char * PubTopicTempOne = "/ Cliente / ESP32 / TempOne"; // Tema primera temperatura
const char * PubTopicTempTwo = "/ Cliente / ESP32 / TempTwo"; // Tema segunda temperatura
const char * PubTopicPresOne = "/ Cliente / ESP32 / PressOne"; // Tema primera presión
const char * PubTopicPresTwo = "/ Cliente / ESP32 / PressTwo"; // Tema segunda presión
const char * PubTopicPotiMap = "/ Cliente / ESP32 / PotiMapValue"; // Tema segunda presión
const char * SUBTOPIC = "/ Cliente / ESP32 / Poti / Valor"; // Valor potencial de suscripción al tema
String clientID = "ESP-DevKit_1"; // Nombre del cliente para el intermediario MQTT

int iLastTempOne, iLastTempTwo, iLastPressOne, iLastPressTwo;

// Crea objetos para mqtt
WiFiClient espClient;
PubSubClient mqttClient (espClient);

#define MSG_BUFFER_SIZE (50)
char msg [MSG_BUFFER_SIZE];

configuración vacía () {
// ponga su código de configuración aquí, para que se ejecute una vez:
Serial.begin (115200);
Serial.println ("prueba BME280");
Serial.println ("Iniciar ambas conexiones I2C");
I2Cone.begin (I2C_SDA1, I2C_SCL1, 400000);
I2Ctwo.begin (I2C_SDA2, I2C_SCL2, 400000);
Serial.println ("Hacer que BME nos hable primero");
bool bStatus;
// Iniciar primer sensor
bStatus = bmeOne.begin (0x76, & I2Cone);
si (! bStatus)
  {
Serial.println ("No se pudo encontrar un sensor BME280 - 1 válido, verifique el cableado");
mientras (1);
  }
más
Serial.println ("¡BME280 válido - 1 sensor!");

// Iniciar segundo sensor
bStatus = bmeTwo.begin (0x76, & I2Ctwo);
si (! bStatus)
  {
Serial.println ("No se pudo encontrar un sensor BME280 - 2 válido, verifique el cableado");
mientras (1);
  }
más
Serial.println ("¡BME280 - 2 sensores válidos!");
writeAdvanceDiag ("Init Wifi", verdadero);
setupWifi ();
writeAdvanceDiag ("Init Wifi - HECHO", verdadero);
writeAdvanceDiag ("Establecer servidor MQTT", verdadero);
mqttClient.setServer (MQTT_BROKER, 1883);
writeAdvanceDiag ("Establecer función de devolución de llamada", verdadero);
mqttClient.setCallback (devolución de llamada);
writeAdvanceDiag ("Finalizar configuración () - Función", verdadero);
  }

bucle vacío () {
// ponga su código principal aquí, para que se ejecute repetidamente:
int iTempOne, iTempTwo, iPressOne, iPressTwo;
si (! mqttClient.connected ())
reconnectMQTT ();

mqttClient.loop ();
// Verifique después de "NEXTUPDATE" si los valores han cambiado
if (millis () - lastTime> NEXTUPDATE)
    {
iTempOne = int (bmeOne.readTemperature ()); // Obtener uno temporal
iTempTwo = int (bmeTwo.readTemperature ()); // Obtener la temperatura dos
iPressOne = int (bmeOne.readPressure () / 100.0F); // Consigue presionar uno
iPressTwo = int (bmeTwo.readPressure () / 100.0F); // presiona dos
if (iTempOne! = iLastTempOne) // Verifique que la temperatura uno haya cambiado y envíe
    {
snprintf (msg, MSG_BUFFER_SIZE, "% 1d", iTempOne); // Convertir mensaje a char
mqttClient.publish (PubTopicTempOne, msg, true); // Enviar al corredor
writeAdvanceDiag ("Enviar Temp uno:" + Cadena (iTempOne), verdadero);
iLastTempOne = iTempOne;
  }
if (iTempTwo! = iLastTempTwo) // Verifique que la temperatura dos haya cambiado y envíe
    {
snprintf (msg, MSG_BUFFER_SIZE, "% 1d", iTempTwo); // Convertir mensaje a char
mqttClient.publish (PubTopicTempTwo, msg, true); // Enviar al corredor
writeAdvanceDiag ("Enviar Temp dos:" + Cadena (iTempTwo), verdadero);
iLastTempTwo = iTempTwo;
    }
if (iPressOne! = iLastPressOne) // Verifique la presión que uno cambió y envíe
    {
snprintf (msg, MSG_BUFFER_SIZE, "% 1d", iPressOne); // Convertir mensaje a char
mqttClient.publish (PubTopicPresOne, msg, true); // Enviar al corredor
writeAdvanceDiag ("Enviar Presione uno:" + Cadena (iPressOne), verdadero);
iLastPressOne = iPressOne;
    }
if (iPressTwo! = iLastPressTwo) // Verifique la presión dos cambiada y envíe
    {
snprintf (msg, MSG_BUFFER_SIZE, "% 1d", iPressTwo); // Convertir mensaje a char
mqttClient.publish (PubTopicPresTwo, msg, true); // Enviar al corredor
writeAdvanceDiag ("Enviar Presione dos:" + Cadena (iPressTwo), verdadero);
iLastPressTwo = iPressTwo;
    }
lastTime = millis ();
  }
}

/*
* =================================================================
* Función: devolución de llamada
* Devoluciones: nulo
* Descripción: se llamará automáticamente, si es un tema suscrito
* tiene un mensaje nuevo
* tema: devuelve el tema, de donde proviene un nuevo mensaje
* payload: el mensaje del tema
* longitud: longitud del mensaje, importante para obtener contenido
* =================================================================
*/
devolución de llamada void (char * topic, byte * payload, unsigned int length)
{
String stMessage = "";
writeAdvanceDiag ("Mensaje recibido del tema:" + Cadena (tema), verdadero);
writeAdvanceDiag ("Longitud del mensaje:" + Cadena (longitud), verdadero);
para (int i = 0; i <longitud; i ++)
stMessage + = String ((char) payload [i]);
writeAdvanceDiag ("El mensaje es:" + stMessage, verdadero);
// Asignar valor y enviar el valor mapeado a mqtt broker
int iValue, iMapValue;
iValue = stMessage.toInt ();
iMapValue = mapa (iValue, 0,1024,0,255);
snprintf (msg, MSG_BUFFER_SIZE, "% 1d", iMapValue); // Convertir mensaje a char
writeAdvanceDiag ("Enviar PotiValue mapeado:" + String (iMapValue), verdadero);
mqttClient.publish (PubTopicPotiMap, msg, true); // Enviar al corredor
}

/*
* =================================================================
* Función: setupWifi
* Devoluciones: nulo
* Descripción: Configurar wifi para conectarse a la red
* =================================================================
*/
configuración vacía Wi-Fi ()
{
Serial.println ("Conexión a:" + Cadena (STASSID));
Modo WiFi (WIFI_STA);
WiFi.begin (STASSID, STAPSK);
mientras (WiFi.status ()! = WL_CONNECTED)
  {
retraso (500);
Serial.print (".");
  }
Serial.println ("");
Serial.println ("WiFi conectado");
Serial.println ("dirección IP:");
Serial.println (WiFi.localIP ());
}

/*
* =================================================================
* Función: writeAdvanceDiag
* Devoluciones: nulo
* Descripción: escribe msg avanzado en el monitor serial, si
* ADVANCEDIAG> = 1
* msg: Mensaje para el monitor serial
* newLine: Mensaje con salto de línea (verdadero)
* =================================================================
*/
void writeAdvanceDiag (String msg, bool newLine)
{
if (bool (ADVANCEDIAG)) // Compruebe si el diagnóstico avanzado está habilitado
{
si (newLine)
Serial.println (msg);
más
Serial.print (msg);
  }
}

/*
* =================================================================
* Función: reconnectMQTT
* Devoluciones: nulo
* Descripción: Si no hay conexión a MQTT, esta función es
* llamado. Además, se registra el tema deseado.
* =================================================================
*/
anular reconectar MQTT ()
{
while (! mqttClient.connected ())
{
writeAdvanceDiag ("Iniciar sesión en el intermediario MQTT", verdadero);
si (mqttClient.connect (clientID.c_str ()))
    {
Serial.println ("Conectado a MQTT Broker" + String (MQTT_BROKER));
writeAdvanceDiag ("Suscribir tema '" + Cadena (SUBTOPIC) + "'", verdadero);
mqttClient.subscribe (SUBTOPIC, 1); // Subtema "SUBTOPIC"
    }
más
    {
writeAdvanceDiag ("Falló con rc =" + String (mqttClient.state ()), true);
Serial.println ("Siguiente MQTT-Connect en 3 segundos");
retraso (3000);
    }
  }
}

 Código 7: Placa de desarrollo WLAN WiFi del módulo ESP32 NodeMCU

En el último paso, se deben visualizar los datos recopilados. El Arduino Uno con escudo ethernet adjunto, la pantalla LCD I2C de 20x04 y un LED monocromo se utilizan para esto.

Puede ver el cableado exacto en Figura 6 ver. Tenga en cuenta que la pantalla LCD I2C de 20x04 requiere una fuente de alimentación de 5V.

 

Figura 6: Arduino Uno Ethernetshield para salida

No hay grandes sorpresas al programar el Arduino Uno. Primero se inicializa la red y se verifica la conexión. En el siguiente paso, se establecen la pantalla y la conexión con el broker MQTT. La función callback () es relevante para la salida de datos, recibe todos los datos del corredor y muestra los cambios en uno o más valores en la pantalla o mediante el LED.

//-----------------------------------------------------
// Ejemplo 3 Arduino Uno Ehternetshiled para recibir
// datos del corredor y se muestran en LCD y LED
// Autor: Joern Weise
// Licencia: GNU GPl 3.0
// Creado: Oct 25, 2020
// Actualización: 26 de octubre de 2020
//-----------------------------------------------------
#include
#include
#include // Lib para MQTT Pub y Sub
#include
#include

#define ADVANCEDIAG 1

LiquidCrystal_I2C lcd (0x27,20,4); // establece la dirección LCD en 0x27 para una pantalla de 16 caracteres y 2 líneas
const int iAnalogOut = 6;
byte mac [] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // dirección MAC para escudo
Dirección IP ip (192, 168, 178, 177); // Dirección IP estática para arduino
IPAddress myDns (192, 168, 178, 1); // enrutador de dirección IP
char server [] = "www.google.com"; // Comprueba si Arduino está en línea

String clientID = "Arduino_Uno"; // Nombre del cliente para el intermediario MQTT
// Temas para suscribirse
const char * MQTT_BROKER = "raspberrypi"; // Nombre del corredor mqtt
const char * SubTopicTempOne = "/ Cliente / ESP32 / TempOne"; // Tema primera temperatura
const char * SubTopicTempTwo = "/ Cliente / ESP32 / TempTwo"; // Tema segunda temperatura
const char * SubTopicPresOne = "/ Cliente / ESP32 / PressOne"; // Tema primera presión
const char * SubTopicPresTwo = "/ Cliente / ESP32 / PressTwo"; // Tema segunda presión
const char * SubTopicPotiMap = "/ Cliente / ESP32 / PotiMapValue"; // Potenciómetro mapeado por tema

// Objetos para ethernet-com
Cliente EthernetClient;
PubSubClient mqttClient (cliente);

// Algunas vars para actualizar
bool bUpdateDisplay = falso;
bool bUpdatePWM = falso;
int iLastTempOne, iLastTempTwo, iLastPressOne, iLastPressTwo, iLastPotiMap;

configuración vacía () {
Serial.begin (115200);
while (! Serial) {
; // espera a que se conecte el puerto serie. Necesario solo para puerto USB nativo
  }
Serial.println ("Monitor Arduino Uno");
pinMode (iAnalogOut, OUTPUT);
Ethernet.init (10); // La mayoría de los escudos Arduino usan el pin digital 10
lcd.init (); // LCD inicial
LCD luz de fondo (); // luz de fondo encendida
lcd.clear (); // Borrar contenido antiguo
bUpdateDisplay = verdadero;
Ethernet.begin (mac, ip); // Iniciar escudo ethernet
// Compruebe si hay hardware Ethernet presente
if (Ethernet.hardwareStatus () == EthernetNoHardware) {
Serial.println ("No se encontró el escudo de Ethernet. Lo sentimos, no se puede ejecutar sin hardware. :(");
while (verdadero) {
retraso (1); // no hacer nada, no tiene sentido ejecutar sin hardware Ethernet
    }
  }
// Comprueba si hay com al enrutador
while (Ethernet.linkStatus () == LinkOFF) {
Serial.println ("El cable Ethernet no está conectado");
retraso (500);
  }

// dale al escudo de Ethernet un segundo para inicializar:
retraso (1000);
Serial.println ("conectando ...");

// Verifique si el sistema puede comunicarse
if (client.connect (servidor, 80)) {
Serial.print ("conectado a");
Serial.println (client.remoteIP ());
// Haz una solicitud HTTP:
client.println ("GET / search? q = arduino HTTP / 1.1");
client.println ("Host: www.google.com");
client.println ("Conexión: cerrar");
cliente.println ();
} más {
// si no obtuvo una conexión con el servidor:
Serial.println ("conexión fallida");
  }
// Iniciar MQTT
writeAdvanceDiag ("Establecer servidor MQTT", verdadero);
mqttClient.setServer (MQTT_BROKER, 1883);
writeAdvanceDiag ("Establecer función de devolución de llamada", verdadero);
mqttClient.setCallback (devolución de llamada);
writeAdvanceDiag ("Finalizar configuración () - Función", verdadero);
}

bucle vacío () {
// ponga su código principal aquí, para que se ejecute repetidamente:
si (! mqttClient.connected ())
reconnectMQTT ();

mqttClient.loop ();

si (bUpdateDisplay)
  {
UpdateDisplay ();
bUpdateDisplay = falso;
  }
si (bUpdatePWM)
  {
analogWrite (iAnalogOut, iLastPotiMap); // Escribe un nuevo valor analógico en LED-Pin
bUpdatePWM = falso;
  }
}

/*
* =================================================================
* Función: writeAdvanceDiag
* Devoluciones: nulo
* Descripción: escribe msg avanzado en el monitor serial, si
* ADVANCEDIAG> = 1
* msg: Mensaje para el monitor serial
* newLine: Mensaje con salto de línea (verdadero)
* =================================================================
*/
void writeAdvanceDiag (String msg, bool newLine)
{
if (bool (ADVANCEDIAG)) // Compruebe si el diagnóstico avanzado está habilitado
  {
si (newLine)
Serial.println (msg);
más
Serial.print (msg);
  }
}

/*
* =================================================================
* Función: reconnectMQTT
* Devoluciones: nulo
* Descripción: Si no hay conexión a MQTT, esta función es
* llamado. Además, se registra el tema deseado.
* =================================================================
*/
anular reconectar MQTT ()
{
while (! mqttClient.connected ())
  {
writeAdvanceDiag ("Iniciar sesión en MQTT-Broker", verdadero);
si (mqttClient.connect (clientID.c_str ()))
    {
Serial.println ("Conectado a MQTT-Broker" + Cadena (MQTT_BROKER));
writeAdvanceDiag ("Suscribir tema '" + String (SubTopicTempOne) + "'", verdadero);
mqttClient.subscribe (SubTopicTempOne, 1); // Subtema "SubTopicTempOne"
writeAdvanceDiag ("Suscribir tema '" + String (SubTopicTempTwo) + "'", verdadero);
mqttClient.subscribe (SubTopicTempTwo, 1); // Tema secundario "SubTopicTempTwo"
writeAdvanceDiag ("Suscribir tema '" + Cadena (SubTopicPresOne) + "'", verdadero);
mqttClient.subscribe (SubTopicPresOne, 1); // Subtema "SubTopicPresOne"
writeAdvanceDiag ("Suscribir tema '" + String (SubTopicPresTwo) + "'", verdadero);
mqttClient.subscribe (SubTopicPresTwo, 1); // Subtema "SubTopicPresTwo"
writeAdvanceDiag ("Suscribir tema '" + String (SubTopicPotiMap) + "'", verdadero);
mqttClient.subscribe (SubTopicPotiMap, 1); // Subtema "SubTopicPotiMap"
    }
más
    {
writeAdvanceDiag ("Falló con rc =" + String (mqttClient.state ()), true);
Serial.println ("Siguiente MQTT-Connect en 3 segundos");
retraso (3000);
    }
  }
}

/*
* =================================================================
* Función: devolución de llamada
* Devoluciones: nulo
* Descripción: se llamará automáticamente, si es un tema suscrito
* tiene un mensaje nuevo
* tema: devuelve el tema, de donde proviene un nuevo mensaje
* payload: el mensaje del tema
* longitud: longitud del mensaje, importante para obtener contenido
* =================================================================
*/
devolución de llamada void (char * topic, byte * payload, unsigned int length)
{
String stMessage = "";
para (int i = 0; i <longitud; i ++)
stMessage + = String ((char) payload [i]);

// Comprueba si la temperatura uno ha cambiado
if (String (tema) == "/ Client / ESP32 / TempOne")
  {
if (iLastTempOne! = stMessage.toInt ())
    {
iLastTempOne = stMessage.toInt ();
bUpdateDisplay = verdadero;
    }
  }
// Comprueba si la temperatura dos ha cambiado
if (String (tema) == "/ Client / ESP32 / TempTwo")
  {
if (iLastTempTwo! = stMessage.toInt ())
    {
iLastTempTwo = stMessage.toInt ();
bUpdateDisplay = verdadero;
    }
  }
// Comprueba si la presión uno ha cambiado
if (String (tema) == "/ Client / ESP32 / PressOne")
  {
if (iLastPressOne! = stMessage.toInt ())
    {
iLastPressOne = stMessage.toInt ();
bUpdateDisplay = verdadero;
    }
  }
// Compruebe si la presión dos ha cambiado
if (String (tema) == "/ Client / ESP32 / PressTwo")
  {
if (iLastPressTwo! = stMessage.toInt ())
    {
iLastPressTwo = stMessage.toInt ();
bUpdateDisplay = verdadero;
    }
  }
// Verifica si el valor de poti mapeado ha cambiado
if (String (tema) == "/ Client / ESP32 / PotiMapValue")
  {
if (iLastPotiMap! = stMessage.toInt ())
    {
iLastPotiMap = stMessage.toInt ();
bUpdatePWM = verdadero;
    }
  }
}

/*
* =================================================================
* Función: UpdateDisplay
* Devoluciones: nulo
* Descripción: Mostrar nuevos valores en I2C-Display
* =================================================================
*/
void UpdateDisplay ()
{
lcd.clear ();
casa lcd ();
lcd.print ("Temp uno [C]:");
lcd.print (iLastTempOne);
lcd.setCursor (0,1);
lcd.print ("Temp dos [C]:");
lcd.print (iLastTempTwo);
lcd.setCursor (0,2);
lcd.print ("Presione uno [hPa]:");
lcd.print (iLastPressOne);
lcd.setCursor (0,3);
lcd.print ("Presione dos [hPa]:");
lcd.print (iLastPressTwo);
}

Código 8: Arduino Uno zur Anzeige der MQTT-Daten

Se trata de todos los códigos verdrahtet und der Code auf alle MicroController überspielt, sollten Sie schnell ein Ergebnis auf dem LCD-Display y der angeschlossenen LED sehen. Beim Drehen vom Potenciómetro wird die Helligkeit der LED geändert und die aktuelle Temperature und Druck beider Sensoren sollten visualisiert werden. Wie das aussehen kann, zeigt Abbildung 7.

Figura 7: Salida de datos del sensor y brillo del LED

Si quieres ver todos los datos de tu broker, te recomiendo la herramienta MQTT.fx. Con él se puede suscribir y publicar datos del corredor. También puede intentar conectar hardware adicional al microcontrolador y enviar los datos al corredor. El Arduino Uno puede, a través de un bucle, mostrar los datos a su vez.

Con la realización de este post, se han dado tanto los fundamentos como un ejemplo práctico más complejo. En la siguiente parte, utilizaremos los fundamentos y el ejemplo práctico para controlar un robot rodante.

Este y otros proyectos pueden encontrarse en GitHub en https://github.com/M3taKn1ght/Blog-Repo

Projekte für anfängerRaspberry pi

3 comentarios

Jörn Weise

Jörn Weise

Hello jm1704,

thank you for the nice compliment. Of course, as bloggers, we are always interested in writing interesting and knowledgeable posts with az-delivery. Of course, the variety must not be missing and the reference to the products. Especially IoT is an interesting topic, but also holds great dangers if the knowledge is missing.
However, it must not be forgotten at this point that the topic is only dealt with on the surface! The broker is not secured and the Pi itself has no significant protection against manipulation. This makes it easy for criminal bodies to do damage when sensitive systems are controlled and regulated.
As my professor in college used to say, “You’ve now learned the basics, do something with it!”

Greetings Weise
-—-IN GERMAN———

Hallo jm1704,

danke für das nette Kompliment. Natürlich sind wir als Blogger immer daran interessiert mit az-delivery interessante und wissenswerte Beiträge zu schreiben. Natürlich darf die Abwechslung nicht fehlen und der Bezug den den Produkten nicht fehlen. Gerade IoT ist ein interessantes Thema, birgt aber auch große Gefahren, wenn das Wissen fehlt.
Man darf an dieser Stelle aber auch nicht vergessen, dass das Thema nur an der Oberfläche behandelt wird! Der Broker ist nicht gesichert und auch der Pi an sich hat keinen nennenswerten Schutz gegen Manipulation. Das macht es für kriminelle Organe einfach, Schaden anzurichten, wenn sensible Systeme kontrolliert und geregelt werden.
Wie mein Professor an der Uni immer sagte, “Sie haben nun die Grundlagen gelernt, machen Sie was drauß!”

Gruß Weise

Dieter Behm

Dieter Behm

Hallo und einen schönen guten Morgen.
Ich habe den ersten Blog glaube ich verstanden und mein Broker läuft auf dem Raspi. Jetzt mein Problem :
Der Node MCU (allerdings V3) lauft , bekommt nur die Verbindung zur FritzBox , gibt im seriellen Monitor keine Ip aus und folgendes erscheint:
-————-

ets Jan 8 2013,rst cause:2, boot mode:(3,6)

load 0×4010f000, len 3584, room 16
tail 0
chksum 0xb0
csum 0xb0
v2843a5ac
~ld
SerialMonitor enabled
Connection to: FRITZ!Box 7590 XQ

CUT HERE FOR EXCEPTION DECODER -——————

Exception (3):
epc1=0×40100718 epc2=0×00000000 epc3=0×00000000 excvaddr=0×4006eb39 depc=0×00000000

>>>stack>>>

Warum verbindet er sich nicht mit dem Broker
Ich hoffe Sie können einem Anfänger aufs Fahrrad helfen
Gruß
Dieter

jm1704

jm1704

Jörn,
Very good article on MQTT with explanatory notes and examples.
By this article you show the know how of AZ-Delivery and its team to use products offered for sale and their integrations.
Thanks

Deja un comentario

Todos los comentarios son moderados antes de ser publicados