esp32开发快速入门 8 : MQTT 的快速入门,基于esp32实现MQTT通信

MQTT 介绍

  1. 简介

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。

esp32开发快速入门 8 : MQTT 的快速入门,基于esp32实现MQTT通信_第1张图片

2. 通信方式

  • MQTT基于客户端-服务器方式,以消息订阅/发布方式传输数据。先说一下几个概念:

  • MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。 代理Broker :它就是MQTT的主体,实际上就是一个服务器,相当于整个MQTT的信息中转站,将发布者的信息转发给订阅者。在实际应用的过程中一般使用云服务器作为代理Broker. 发布者(Publish):顾名思义就是消息的发布者,它负责把消息通过(Publish)方式发送到Broker服务器上,剩下的它就不管了。 订阅者(Subscribe):订阅者则是消息接收方,当服务器接收到发布者发布的信息后,在服务器中查找如果发现有订阅者订阅了这条信息,那么服务器就把这条信息发送给订阅者。

  • 那么这三者通过什么方式判断消息的区别那?那又引出来下一个概念主题(Topic)和负载(Payload). Topic:可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload) payload:可以理解为消息的内容,是指订阅者具体要使用的内容。

通俗上讲就是 :发布者和订阅者约定好一个标题(主题topic)并且把这个标题存储在服务器上,发布者把消息内容(负载(Payload))发布到服务器这个标题下面,订阅者从服务器订阅这个标题。 当服务器发现这个标题下面有内容了就开始查找谁订阅这个标题,查找到了之后就把消息内容发送给订阅者 。

程序介绍

esp-idf 中有专门介绍MQTT的相关例程,例程位于esp-idf/examples/protocols/mqtt中,乐鑫提供了多个例程

esp32开发快速入门 8 : MQTT 的快速入门,基于esp32实现MQTT通信_第2张图片

我们选择tcp例程讲解,其他例程原理上是一样,例程中连接的服务器是eclipse 专门提供的MQTT的免费服务器,用户可以使用这个服务器进行测试。

1. 主函数

void app_main()
    {
        /*输出log 相关配置 我们不需要关心*/
        ESP_LOGI(TAG, "[APP] Startup..");
        /*获取空闲内存大小*/
        ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
        /*打印当前idf的版本*/
        ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
        /*配置打印信息*/
        esp_log_level_set("*", ESP_LOG_INFO);
        esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
        esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE);
        esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE);
        esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
        esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE);

        /*flash 初始化,tcp/ip通信时 需要使用flash*/
        nvs_flash_init();
        /*wifi初始化*/
        wifi_init();
        /*mqtt开始运行*/
        mqtt_app_start();
    }

2. wifi函数注释

static void wifi_init(void)
    {
        tcpip_adapter_init();//tcpip 协议栈初始化,使用网络时必须调用此函数
        /*创建一个freeRTOS的事件标志组,用于当wifi没有连接时将程序停下,只有wifi连接成功了才能继续运行程序*/
        wifi_event_group = xEventGroupCreate();
        /*配置 wifi的回调函数,用于连接wifi*/
        /*
        * ESP_ERROR_CHECK检查函数返回值
        */
        ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL));
        /*wifi配置*/
        wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
        ESP_ERROR_CHECK(esp_wifi_init(&cfg));
        ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
        wifi_config_t wifi_config = {
            .sta = {
                .ssid = CONFIG_WIFI_SSID,
                .password = CONFIG_WIFI_PASSWORD,
            },
        };
        /*设置wifi 为sta模式*/
        ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
        ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
        ESP_LOGI(TAG, "start the WIFI SSID:[%s]", CONFIG_WIFI_SSID);
        /*开始运行wifi*/
        ESP_ERROR_CHECK(esp_wifi_start());
        ESP_LOGI(TAG, "Waiting for wifi");
        /*等待事件标志,成功获取到事件标志位后才继续执行,否则一直等在这里*/
        xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
    }
    /*wifi 的中断回调函数,检测wifi的事件标志位*/
    static esp_err_t wifi_event_handler(void *ctx, system_event_t *event)
    {
        switch (event->event_id) {
            case SYSTEM_EVENT_STA_START://开始执行station 
                esp_wifi_connect();//根据wifi配置,连接wifi
                break;
            case SYSTEM_EVENT_STA_GOT_IP://成功获取到ip,表示联网成功
                xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);//设置事件标志位,程序继续运行

                break;
            case SYSTEM_EVENT_STA_DISCONNECTED://station 已经断开了,重新连接wifi
                esp_wifi_connect();
                xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
                break;
            default:
                break;
        }
        return ESP_OK;
    }

3. mqtt函数注释

static void mqtt_app_start(void)
{
    /*配置mqtt ,broker 地址 和mqtt的事件回调函数*/
    esp_mqtt_client_config_t mqtt_cfg = {
        .uri = CONFIG_BROKER_URL,
        .event_handle = mqtt_event_handler,
        // .user_context = (void *)your_context
    };

    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);//出事mqtt的相关配置
    esp_mqtt_client_start(client);//开始执行mqtt
}




static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
{
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    // your_context_t *context = event->context;
    switch (event->event_id) {
        case MQTT_EVENT_CONNECTED://MQTT 已连接
            ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
            /*发布主题/topic/qos1*/
            msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
            ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
            /*订阅主题/topic/qos0 */
            msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
            /*订阅主题/topic/qos1*/
            msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
            /*取消订阅/topic/qos1*/
            msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
            ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
            break;
        case MQTT_EVENT_DISCONNECTED://MQTT断开连接
            ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
            break;

        case MQTT_EVENT_SUBSCRIBED://MQTT收到订阅信息
            ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
            msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
            ESP_LOGI(TAG, "sent publish successful, msg_id=%d", 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;
    }
    return ESP_OK;
}

写在最后

至此 MQTT的简单流程就介绍完了,MQTT之所以在物联网上应用比较广泛就是因为它的简单,易用在加上它数据精简正好适合物联网通信的要求。

现在国内外的一线大厂阿里,华为,百度 ,亚马逊 的服务器都支持MQTT协议,如果想在实际的应用使用MQTT的话,主要是研究各个厂家服务器上的其他设置,比如连接方式,检验机制,如何配置选项等,MQTT本身这一块没有什么东西。

希望此文对大家能有所帮助。

欢迎关注我的个人网站:zwww.zcxbb.com

知乎专栏:物联网开发入门 - 知乎 (zhihu.com)

你可能感兴趣的:(ESP32开发快速入门,服务器,网络,运维)