ESP-IDF: Utilizando MQTT em seus projetos

29/01/2024 16:35 Comentário(s) Por Carlos Korovsky

O MQTT (Message Queuing Telemetry Transport) emerge como uma poderosa solução de comunicação para o mundo interconectado de dispositivos na era da Internet das Coisas (IoT). Este protocolo leve e eficiente foi projetado para facilitar a troca de mensagens entre dispositivos distribuídos em redes com larguras de banda variadas. Com sua arquitetura baseada em publicação/assinatura, o MQTT proporciona uma comunicação assíncrona, permitindo que dispositivos troquem informações de forma eficaz e sem a necessidade de conexões persistentes. 
Uma das principais vantagens do uso do MQTT é a sua baixa sobrecarga de rede, o que o torna ideal para dispositivos com recursos limitados, como micro controladores. Além disso, o MQTT é altamente escalável e flexível, permitindo a comunicação entre milhares de dispositivos simultaneamente.
Para utilizar o MQTT em seus projetos baseados no ESP-IDF, é necessário primeiro configurar um broker MQTT, que será responsável por receber e encaminhar as mensagens entre os dispositivos. Em seguida, você pode utilizar a biblioteca MQTT do ESP-IDF para se conectar ao broker, publicar e assinar tópicos e trocar informações com outros dispositivos.
Ao utilizar o MQTT em seus projetos, você poderá criar aplicações IoT robustas e eficientes, permitindo que seus dispositivos se comuniquem de forma confiável e escalável.

Broker

Para fins didaticos neste tutorial utilizaremos o broker de testes gratuito disponível no endereço:
[broker.emqx.io] na porta 1883.

Objetivo

A aplicação ira conectar ao WiFi obeter o endereço IP via DHCP, em seguida a mesma ira se increver no topico MQTT e publicar uma mensagem, além de imprimir no monitor serial as mensagens recebidas no mesmo topico.

Código

A execução inicial do código é conduzida pela função wifi_app_start(). Essa função tem a responsabilidade de iniciar o Wi-Fi, estabelecer a conexão com a internet e, adicionalmente, através de uma função de callback configurada para ser acionada após o ESP receber um endereço IP válido, estabelecer a conexão com o MQTT.


static void wifi_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    if (event_id == WIFI_EVENT_STA_START)
    {
        ESP_LOGI(TAG, "Tentando conexao wifi....\n");
    }
    else if (event_id == WIFI_EVENT_STA_CONNECTED)
    {
        ESP_LOGI(TAG, "WiFi conenctado");
    }
    else if (event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        ESP_LOGI(TAG, "WiFi desconectado\n");
        if (retry_num < 5) { esp_wifi_connect(); retry_num++; ESP_LOGI(TAG, "Tentando reconexao wifi...\n"); } } else if (event_id== IP_EVENT_STA_GOT_IP) { ESP_LOGI(TAG, "IP recebido"); //Inicia procedimento de conexão ao MQTT mqtt_app_start(); } } esp_err_t wifi_app_start() { //Inicializa o armazenamento flash (requerido pelo driver de wifi) ESP_ERROR_CHECK(nvs_flash_init()); //Inicializa o sistema Wi-fi e a pilha TCP/IP esp_netif_init(); esp_event_loop_create_default(); esp_netif_create_default_wifi_sta(); wifi_init_config_t wifi_initiation=WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&wifi_initiation); //Registra as funçoes de callback para os eventos de wifi e ip esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL); esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL); //Estrutura de incialização wifi_config_t wifi_configuration={ .sta={ .ssid="" , .password="" , } }; //Faz uma cópia das variaveis globais para dentro da estrutura de inicialização strcpy((char*)wifi_configuration.sta.ssid, ssid); strcpy((char*)wifi_configuration.sta.password, passwd); //Atribui os parametros de inicialização esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_configuration); //Inicializa a conexão esp_wifi_start(); esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_connect(); return ESP_OK; }

A função mqtt_app_start() é invocada para configurar o endereço do broker MQTT e definir a função de callback responsável por gerenciar os eventos associados. Após a conexão bem-sucedida com o broker, o ESP se inscreve em um tópico específico e envia uma mensagem correspondente. Adicionalmente, o conteúdo desse tópico é impresso no monitor serial.  


static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;
    switch ((esp_mqtt_event_id_t)event_id)
    {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");

        char *topico = "testtopic/238191212838";
        char *mensagem = "Olá mundo";

        esp_mqtt_client_publish(handler_args, topico, mensagem, 0, 0 ,0 );
        esp_mqtt_client_subscribe_single(handler_args, topico, 0);

        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
        break;
    case MQTT_EVENT_SUBSCRIBED:
        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(TAG, "Dados recebidos: ");
        printf("TOPICO=%.*s\r\n", event->topic_len, event->topic);
        printf("DADOS=%.*s\r\n", event->data_len, event->data);
        break;
    default:
        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
        break;
    }
}

static void mqtt_app_start(void)
{

    esp_mqtt_client_config_t mqtt_cfg = {
        .broker.address.uri = "mqtt://broker.emqx.io:1883",
    };

    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);

    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
    esp_mqtt_client_start(client);
}

Resultado

Usando o cliente MQTTX para interagir com o broker:

Mensagem enviada pelo computador:

Mensagem recebida no ESP:

Bônus: Código completo:


#include 
#include 
#include 
#include 
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"

#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"

#include "esp_log.h"
#include "mqtt_client.h"


char *ssid = "Nome canonico da rede wifi";
char *passwd = "senha";

static const char *TAG = "EXEMPLO_MQTT";
int retry_num = 0;

static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;
    switch ((esp_mqtt_event_id_t)event_id)
    {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");

        char *topico = "testtopic/238191212838";
        char *mensagem = "Olá mundo";

        esp_mqtt_client_publish(handler_args, topico, mensagem, 0, 0 ,0 );
        esp_mqtt_client_subscribe_single(handler_args, topico, 0);

        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
        break;
    case MQTT_EVENT_SUBSCRIBED:
        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(TAG, "Dados recebidos: ");
        printf("TOPICO=%.*s\r\n", event->topic_len, event->topic);
        printf("DADOS=%.*s\r\n", event->data_len, event->data);
        break;
    default:
        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
        break;
    }
}

static void mqtt_app_start(void)
{

    esp_mqtt_client_config_t mqtt_cfg = {
        .broker.address.uri = "mqtt://broker.emqx.io:1883",
    };

    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);

    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
    esp_mqtt_client_start(client);
}

static void wifi_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    if (event_id == WIFI_EVENT_STA_START)
    {
        ESP_LOGI(TAG, "Tentando conexao wifi....\n");
    }
    else if (event_id == WIFI_EVENT_STA_CONNECTED)
    {
        ESP_LOGI(TAG, "WiFi conenctado");
    }
    else if (event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        ESP_LOGI(TAG, "WiFi desconectado\n");
        if (retry_num < 5) { esp_wifi_connect(); retry_num++; ESP_LOGI(TAG, "Tentando reconexao wifi...\n"); } } else if (event_id== IP_EVENT_STA_GOT_IP) { ESP_LOGI(TAG, "IP recebido"); //Inicia procedimento de conexão ao MQTT mqtt_app_start(); } } esp_err_t wifi_app_start() { //Inicializa o armazenamento flash (requerido pelo driver de wifi) ESP_ERROR_CHECK(nvs_flash_init()); //Inicializa o sistema Wi-fi e a pilha TCP/IP esp_netif_init(); esp_event_loop_create_default(); esp_netif_create_default_wifi_sta(); wifi_init_config_t wifi_initiation=WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&wifi_initiation); //Registra as funçoes de callback para os eventos de wifi e ip esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL); esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL); //Estrutura de incialização wifi_config_t wifi_configuration={ .sta={ .ssid="" , .password="" , } }; //Faz uma cópia das variaveis globais para dentro da estrutura de inicialização strcpy((char*)wifi_configuration.sta.ssid, ssid); strcpy((char*)wifi_configuration.sta.password, passwd); //Atribui os parametros de inicialização esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_configuration); //Inicializa a conexão esp_wifi_start(); esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_connect(); return ESP_OK; } void app_main(void) { esp_log_level_set("*", ESP_LOG_NONE); ESP_ERROR_CHECK(wifi_app_start()); }
Sobre o autor:
Matheus desempenha o papel de desenvolvedor de sistemas de tecnologia da informação dentro da equipe da UKTech. Possui  experiência no domínio de micro controladores e desenvolvimento de software, e está na reta final de sua graduação em Engenharia de Computação. Em seus momentos de lazer, ele se dedica a interesses como jogos de computador e a apreciação de animes.
Partilhar -