ESP32与MQTT:物联网通信的完美搭档


ESP32 是由 Espressif 开发的一系列低成本低功耗系统级芯片(SoC)微控制器,包括 Wi-Fi 和蓝牙无线功能以及双核处理器。如果你熟悉 ESP8266,那么 ESP32 就是它的继任者,拥有许多新功能。


在物联网领域,基于ESP32开发是一个非常不错的选择,广泛用于智能家居、智能农业及工业物联网等行业。



而MQTT作为物联网通信技术的首选,两者结合,简直完美!我们这期讨论如何在ESP32中应用MQTT?



ESP32



为什么要使用ESP32,以下是它的主要优点:


  • 低成本

  • 价格实惠:在几大电商平台,可以以几十块的价格购买 ESP32,这使其对普通公众来说非常容易获取。


  • 高性能处理能力

    • 双核处理器:ESP32 配备了双核 Xtensa LX6 微处理器,主频高达 240MHz,能够同时处理多个任务,提高系统的响应速度和效率。例如,在复杂的传感器数据处理和通信任务中,双核可以分别处理不同的任务,一个核心处理传感器数据采集,另一个核心处理网络通信,互不干扰。

    • 丰富的外设接口:它支持多种外设接口,如 SPI、I2C、UART、I2S 等,可以方便地连接各种传感器、存储器和其他外设,满足不同应用场景的需求。

  • 强大的无线通信能力

    • Wi-Fi 和蓝牙双模:ESP32 支持 2.4GHz 802.11 b/g/n Wi-Fi 和蓝牙 4.2 BLE,能够实现高速无线数据传输和低功耗蓝牙通信。这使得设备可以通过 Wi-Fi 连接到互联网,同时利用蓝牙与其他设备进行近距离通信,如智能手机、智能手表等。

    • 良好的网络连接性:它支持多种网络协议,如 TCP/IP、HTTP、MQTT 等,可以方便地与云平台或其他物联网设备进行通信,实现设备之间的数据交互和协同工作。

  • 低功耗设计

    • 多种低功耗模式:ESP32 提供了多种低功耗模式,如睡眠模式、深度睡眠模式和轻睡眠模式,可以根据不同的应用场景和任务需求灵活选择,有效降低设备的功耗。例如,在传感器节点中,设备可以在大部分时间处于深度睡眠模式,仅在需要采集数据和传输数据时唤醒,大大延长了电池的使用寿命。

    • 高效的电源管理:它内置了高效的电源管理系统,能够自动调节芯片的工作电压和电流,进一步降低功耗。这对于需要长时间运行且依赖电池供电的物联网设备来说非常重要。

  • 高集成度和小尺寸

    • 高度集成:ESP32 将微处理器、无线通信模块、多种外设接口和电源管理等功能集成在一个芯片上,大大减少了外部元件的数量和电路板的面积,降低了硬件成本和设计复杂度。

    • 小尺寸封装:它采用了多种小尺寸封装形式,如 QFN、BGA 等,适合用于对空间要求较高的小型物联网设备,如智能手环、智能标签等。

  • 丰富的开发资源和社区支持

    • 开源开发环境:Espressif 提供了开源的开发环境,如 ESP-IDF(Espressif IoT Development Framework),支持多种编程语言,如 C、Python 等,方便开发者进行开发和调试。

    • 强大的社区支持:ESP32 拥有一个活跃的开发者社区,社区成员分享了大量的代码示例、开发经验和解决方案,开发者可以方便地获取技术支持和帮助,加速开发进程。



ESP32如何应用MQTT?



我们在应用之前,首先要了解MQTT的几个核心概念:

MQTT通信的核心:Topic主题

MQTT发布和订阅架构

MQTT 客户端和MQTT 代理!!!


通过整理我们这期介绍两种平台来如何使用 ESP32 通过 MQTT 协议向 MQTT 代理发送和接收数据:


  • Arduino IDE

  • VS Code + ESP-IDF



图片

Arduino IDE



首先,我们需要安装Arduino开发环境:

基本的步骤:

  • 将 ESP32 板连接到本地网络

  • 确定好网络OK
  • 将 ESP32 连接到 MQTT 代理
  • 将 MQTT 消息发布到 MQTT 主题


我们这里不做详细说明,有开发经验的朋友欢迎补充,具体的可以参考文末的链接【1】。


当然,我们把内部的代码贴在下方:

/* * This ESP32 code is created by esp32io.com * * This ESP32 code is released in the public domain * * For more detail (instruction and wiring diagram), visit https://esp32io.com/tutorials/esp32-mqtt */
#include <WiFi.h>#include <MQTTClient.h>#include <ArduinoJson.h>
#define CLIENT_ID "ESP32-001"  // CHANGE IT AS YOU DESIRE
const char WIFI_SSID[] = "YOUR_WIFI_SSID";              // CHANGE TO YOUR WIFI SSIDconst char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";           // CHANGE TO YOUR WIFI PASSWORDconst char MQTT_BROKER_ADRRESS[] = "192.168.0.26";  // CHANGE TO MQTT BROKER'S IP ADDRESSconst int MQTT_PORT = 1883;const char MQTT_USERNAME[] = "";  // CHANGE IT IF REQUIREDconst char MQTT_PASSWORD[] = "";  // CHANGE IT IF REQUIRED
// The MQTT topics that this device should publish/subscribe#define PUBLISH_TOPIC "esp32-001/send"#define SUBSCRIBE_TOPIC "esp32-001/receive"
#define PUBLISH_INTERVAL 5000  // 4 seconds
WiFiClient network;MQTTClient mqtt = MQTTClient(256);
unsigned long lastPublishTime = 0;
void setup() {  Serial.begin(9600);
  // set the ADC attenuation to 11 dB (up to ~3.3V input)  analogSetAttenuation(ADC_11db);  WiFi.mode(WIFI_STA);  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.println("ESP32 - Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {    delay(500);    Serial.print(".");  }  Serial.println();
  connectToMQTT();}
void loop() {  mqtt.loop();
  if (millis() - lastPublishTime > PUBLISH_INTERVAL) {    sendToMQTT();    lastPublishTime = millis();  }}
void connectToMQTT() {  // Connect to the MQTT broker  mqtt.begin(MQTT_BROKER_ADRRESS, MQTT_PORT, network);
  // Create a handler for incoming messages  mqtt.onMessage(messageHandler);
  Serial.print("ESP32 - Connecting to MQTT broker");
  while (!mqtt.connect(CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) {    Serial.print(".");    delay(100);  }  Serial.println();
  if (!mqtt.connected()) {    Serial.println("ESP32 - MQTT broker Timeout!");    return;  }
  // Subscribe to a topic, the incoming messages are processed by messageHandler() function  if (mqtt.subscribe(SUBSCRIBE_TOPIC))    Serial.print("ESP32 - Subscribed to the topic: ");  else    Serial.print("ESP32 - Failed to subscribe to the topic: ");
  Serial.println(SUBSCRIBE_TOPIC);  Serial.println("ESP32  - MQTT broker Connected!");}
void sendToMQTT() {  StaticJsonDocument<200> message;  message["timestamp"] = millis();  message["data"] = analogRead(0);  // Or you can read data from other sensors  char messageBuffer[512];  serializeJson(message, messageBuffer);
  mqtt.publish(PUBLISH_TOPIC, messageBuffer);
  Serial.println("ESP32 - sent to MQTT:");  Serial.print("- topic: ");  Serial.println(PUBLISH_TOPIC);  Serial.print("- payload:");  Serial.println(messageBuffer);}
void messageHandler(String &topic, String &payload) {  Serial.println("ESP32 - received from MQTT:");  Serial.println("- topic: " + topic);  Serial.println("- payload:");  Serial.println(payload);
  // You can process the incoming data as json object, then control something  /*  StaticJsonDocument<200> doc;  deserializeJson(doc, payload);  const char* message = doc["message"];  */}

上述ESP32 代码的作用是:

  • 连接到 MQTT 代理

  • 订阅一个主题

  • 定期发布消息到另一个主题



VS CODE+ESP-IDF



下面我们将介绍如何使用 ESP32 作为 ESP-IDF mqtt_client 库的 MQTT 客户端。

主要的步骤:

  • 首先,我们将看到如何将 ESP32 作为客户端连接到 Mosquito MQTT 代理。

  • 其次,我们将学习如何使用 ESP-IDF 中可用的 mqtt_client 库将 ESP32 连接到 MQTT 代理,订阅不同的主题,以及如何在不同的主题上发布消息。

  • 为了演示,我们将使用 MQTTx 客户端桌面应用程序。我们将使用 MQTTx 订阅 MQTT 主题并发布消息。


我们将在 Raspberry Pi 上安装 Mosquitto MQTT 代理,但你也可以在你的 Windows 和 Linux 机器上安装它。当然,也可以选择其他的代理:

Top 5 免费开源MQTT Brokers代理!!!

详细的步骤可参考文末的链接【2】,我们这里把主题代码贴在下方:

#include <stdio.h>#include <stdint.h>#include <stddef.h>#include <string.h>#include "esp_wifi.h"#include "esp_system.h"#include "nvs_flash.h"#include "esp_event.h"#include "esp_netif.h"#include "protocol_examples_common.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"static const char *TAG = "MQTT_EXAMPLE";#define  EXAMPLE_ESP_WIFI_SSID "YOUR_SSID"#define  EXAMPLE_ESP_WIFI_PASS "YOUR_PASSWORD"
uint32_t MQTT_CONNEECTED = 0;static void mqtt_app_start(void);static esp_err_t event_handler(void *ctx, system_event_t *event){    switch (event->event_id)    {    case SYSTEM_EVENT_STA_START:        esp_wifi_connect();        ESP_LOGI(TAG, "Trying to connect with Wi-Fi\n");        break;    case SYSTEM_EVENT_STA_CONNECTED:        ESP_LOGI(TAG, "Wi-Fi connected\n");        break;    case SYSTEM_EVENT_STA_GOT_IP:        ESP_LOGI(TAG, "got ip: startibg MQTT Client\n");        mqtt_app_start();        break;    case SYSTEM_EVENT_STA_DISCONNECTED:        ESP_LOGI(TAG, "disconnected: Retrying Wi-Fi\n");        esp_wifi_connect();        break;    default:        break;    }    return ESP_OK;}void wifi_init(void){    ESP_ERROR_CHECK(esp_netif_init());    ESP_ERROR_CHECK(esp_event_loop_create_default());    esp_netif_create_default_wifi_sta();    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();    ESP_ERROR_CHECK(esp_wifi_init(&cfg));    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));    wifi_config_t wifi_config = {        .sta = {            .ssid = EXAMPLE_ESP_WIFI_SSID,            .password = EXAMPLE_ESP_WIFI_PASS,     .threshold.authmode = WIFI_AUTH_WPA2_PSK,        },    };    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );    ESP_ERROR_CHECK(esp_wifi_start());}/* * @brief Event handler registered to receive MQTT events * *  This function is called by the MQTT client event loop. * * @param handler_args user data registered to the event. * @param base Event base for the handler(always MQTT Base in this example). * @param event_id The id for the received event. * @param event_data The data for the event, esp_mqtt_event_handle_t. */static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data){    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);    esp_mqtt_event_handle_t event = event_data;    esp_mqtt_client_handle_t client = event->client;    int msg_id;    switch ((esp_mqtt_event_id_t)event_id)    {    case MQTT_EVENT_CONNECTED:        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");        MQTT_CONNEECTED=1;
        msg_id = esp_mqtt_client_subscribe(client, "/topic/test1", 0);        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);        msg_id = esp_mqtt_client_subscribe(client, "/topic/test2", 1);        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);        break;    case MQTT_EVENT_DISCONNECTED:        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");        MQTT_CONNEECTED=0;        break;    case MQTT_EVENT_SUBSCRIBED:        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);        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, "MQTT_EVENT_DATA");        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);        printf("DATA=%.*s\r\n", event->data_len, event->data);        break;    case MQTT_EVENT_ERROR:        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");        break;    default:        ESP_LOGI(TAG, "Other event id:%d", event->event_id);        break;    }}esp_mqtt_client_handle_t client = NULL;static void mqtt_app_start(void){    ESP_LOGI(TAG, "STARTING MQTT");    esp_mqtt_client_config_t mqttConfig = {        .uri = "mqtt://192.168.1.4:1883"};
    client = esp_mqtt_client_init(&mqttConfig);    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);    esp_mqtt_client_start(client);}void Publisher_Task(void *params){  while (true)  {    if(MQTT_CONNEECTED)    {        esp_mqtt_client_publish(client, "/topic/test3", "Helllo World", 0, 0, 0);        vTaskDelay(15000 / portTICK_PERIOD_MS);    }  }}void app_main(void){    esp_err_t ret = nvs_flash_init();    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {      ESP_ERROR_CHECK(nvs_flash_erase());      ret = nvs_flash_init();    }    ESP_ERROR_CHECK(ret);    wifi_init();    xTaskCreate(Publisher_Task, "Publisher_Task", 1024 * 5, NULL, 5, NULL);}
详细代码:

https://github.com/ESP32Tutorials/esp32-mqtt-pub-sub-esp-idf

关于本期内容,重要是实践,要完全搞懂这些,可以参考下方详细的链接,包含上述没有细化的内容。如果各位同仁有更好的经验欢迎在留言区分享哦!

参考链接:


  1. https://esp32io.com/tutorials/esp32-mqtt

  2. https://esp32tutorials.com/esp32-mqtt-client-publish-subscribe-esp-idf/

  3. https://randomnerdtutorials.com/getting-started-with-esp32/

  4. https://www.theengineeringprojects.com/2021/11/esp32-mqtt.html

  5. https://microcontrollerslab.com/esp32-mqtt-publish-subscribe-arduino-ide/

  6. https://www.arduino.cc/en/software/



图片


图片

——–END——–

图片
喜欢本文请分享及“点赞”和“在看”

发表评论