MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅
(publish/subscribe
)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。
MQTT最大优点在于,用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。
作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。
MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。
简单介绍一下MQTT协议,关于具体的报文读者可以自行了解,本文在于应用。
- /*Broker Address:${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com*/
- #define Aliyun_host "iot-06z00f69gov61mh.mqtt.iothub.aliyuncs.com"
- #define Aliyun_port 1883
- /*Client ID: ${ClientID}|securemode=${Mode},signmethod=${SignMethod}|*/
- #define Aliyun_client_id "ic3c6cpfq6I.esp32s3|securemode=2,signmethod=hmacsha256,timestamp=1691813578260|"
- /*User Name: ${DeviceName}&${ProductKey}*/
- #define Aliyun_username "esp32s3&ic3c6cpfq6I"
- /*使用官网 MQTT_Password 工具生成*/
- #define Aliyun_password "18a216e310cf5c7337f04f8f0ea2243707b960007c6cd64e83e811f1a13af0e7"
-
- #define AliyunSubscribeTopic_user_get "/ic3c6cpfq6I/esp32s3/user/get"
- #define AliyunPublishTopic_user_update "/ic3c6cpfq6I/esp32s3/user/update"
- #define AliyunSubscribeTopic_post "/sys/ic3c6cpfq6I/esp32s3/thing/event/property/post"
- #define AliyunSubscribeTopic_post_reply "/sys/ic3c6cpfq6I/esp32s3/thing/event/property/post_reply"
这是MQTT连接参数,我们可以去阿里云中的物联网平台中的产品参数中找到。
-
- char mqtt_message[256]={0};
- char mqtt_publish_data1[] = "mqtt connect ok ";
- char mqtt_publish_data2[] = "mqtt subscribe successful";
- char mqtt_publish_data3[] = "mqtt i am esp32";
-
- esp_mqtt_client_handle_t client;
-
- static esp_err_t mqtt_event_handler_cb(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:
- ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
- msg_id = esp_mqtt_client_publish(client, AliyunPublishTopic_user_update, mqtt_publish_data1, strlen(mqtt_publish_data1), 1, 0);
- ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
-
- msg_id = esp_mqtt_client_subscribe(client, AliyunSubscribeTopic_user_get, 0);
- ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
-
- break;
- case MQTT_EVENT_DISCONNECTED:
- ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
- break;
-
- case MQTT_EVENT_SUBSCRIBED:
- ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
- msg_id = esp_mqtt_client_publish(client, AliyunPublishTopic_user_update, mqtt_publish_data2, strlen(mqtt_publish_data2), 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;
- }
-
- 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);
- mqtt_event_handler_cb(event_data);
- }
-
- static void mqtt_test_task(void *pvParameters)
- {
- uint8_t num = 0;
-
- while(1)
- {
- esp_mqtt_client_publish(client, AliyunPublishTopic_user_update, mqtt_publish_data3, strlen(mqtt_publish_data3), 1, 0);
- vTaskDelay(2000 / portTICK_PERIOD_MS);
- if(num++ > 2) break;
- }
- vTaskDelete(NULL);
- }
这是连云的相关函数,我们通过espidf中的日志打印出我们执行的操作。同样的创建一个线程来进行连云。
- void user_mqtt_app_start(void)
- {
-
- esp_mqtt_client_config_t mqtt_cfg = {
- .host = Aliyun_host,
- .port = Aliyun_port,
- .client_id = Aliyun_client_id,
- .username = Aliyun_username,
- .password = Aliyun_password,
-
- };
-
- 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);
- xTaskCreate(&mqtt_test_task, "mqtt_test_task", 4096, NULL, 5, NULL);
- }
这是我们在mqtt.c里面的头文件,创建一个客户端,通过mqtt参数连接至阿里云。
- sprintf(mqtt_message,"{\"method\":\"thing.event.property.post\",\"params\":{\"temperature\":%d,\"Humidity\":%d,\"LightLux\":%d,\"soilHumidity\":%d,\"WaterOutletSwitch\":%d,\"Rain\":%d,},\"version\":\"1.1.1\"}",
- Temp,Humi,light,Soil, Bool, Rain);
-
- esp_mqtt_client_publish(client, AliyunSubscribeTopic_post, mqtt_message, strlen(mqtt_message), 0, 0);//上传
这是我们的数据上传代码,我们通过#include "mqtt_client.h"这个头文件中的函数,将我们要上传的数据通过sprintf这个函数形成一个字符串,然后通过esp_mqtt_client_publish把数据上传至阿里云。
-
- static void event_handler(void* arg, esp_event_base_t event_base,
- int32_t event_id, void* event_data)
- {
- if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
- esp_wifi_connect();
- } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
- if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
- esp_wifi_connect();
- s_retry_num++;
- ESP_LOGI(TAG, "retry to connect to the AP");
- } else {
- xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
- }
- ESP_LOGI(TAG,"connect to the AP fail");
- } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
- ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
- ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
- s_retry_num = 0;
- xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
- }
- }
-
- void wifi_init_sta(void)
- {
-
- s_wifi_event_group = xEventGroupCreate();
-
- /*Initialize the underlying TCP/IP stack.
- NOTE:This function should be called exactly once from application code, when the application starts up. */
- ESP_ERROR_CHECK(esp_netif_init());
-
- /*Create default event loop. */
- ESP_ERROR_CHECK(esp_event_loop_create_default());
-
- /*Creates default WIFI STA. In case of any init error this API aborts. */
- esp_netif_create_default_wifi_sta();
-
- /*Init WiFi Alloc resource for WiFi driver, such as WiFi control structure, RX/TX buffer, WiFi NVS structure etc,
- this WiFi also start WiFi task. */
- wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
- ESP_ERROR_CHECK(esp_wifi_init(&cfg));
-
- esp_event_handler_instance_t instance_any_id;
- esp_event_handler_instance_t instance_got_ip;
- ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
- ESP_EVENT_ANY_ID,
- &event_handler,
- NULL,
- &instance_any_id));
- ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
- IP_EVENT_STA_GOT_IP,
- &event_handler,
- NULL,
- &instance_got_ip));
-
- wifi_config_t wifi_config = {
- .sta = {
- .ssid = EXAMPLE_ESP_WIFI_SSID,
- .password = EXAMPLE_ESP_WIFI_PASS,
- /* Setting a password implies station will connect to all security modes including WEP/WPA.
- * However these modes are deprecated and not advisable to be used. Incase your Access point
- * doesn't support WPA2, these mode can be enabled by commenting below line */
- .threshold.authmode = WIFI_AUTH_WPA2_PSK,
-
- .pmf_cfg = {
- .capable = true,
- .required = false
- },
- },
- };
- ESP_ERROR_CHECK(esp_wifi_set_mode( WIFI_MODE_APSTA) );
- ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
- ESP_ERROR_CHECK(esp_wifi_start() );
-
- ESP_LOGI(TAG, "wifi_init_sta finished.");
-
- /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
- * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
- EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
- WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
- pdFALSE,
- pdFALSE,
- portMAX_DELAY);
-
- /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
- * happened. */
- if (bits & WIFI_CONNECTED_BIT) {
- ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
- EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
- } else if (bits & WIFI_FAIL_BIT) {
- ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
- EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
- } else {
- ESP_LOGE(TAG, "UNEXPECTED EVENT");
- }
-
- /* The event will not be processed after unregister */
- ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
- ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
- vEventGroupDelete(s_wifi_event_group);
-
-
- }
我们首先创建一个事件处理函数,但我们没用连接上wifi时,便会在控制台日志一直打印AP fail。
下面的就是我们把esp32s3设置成sta模式,连接WiFi,然后打印出ip地址。
注意:我们单片机连接的WiFi最好都设置成2.4G频段的,不然可能会连接不上或者不稳定。
- void app_main(void)
- {
-
-
-
-
- //Initialize NVS
- 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);
- ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
- //example_wifi_init();
- wifi_init_sta();
- user_mqtt_app_start();
- while(1)
- {
-
- vTaskDelay(200);
-
- break;
- }
-
- }
这样我们便可以通过esp32s3自带的wifi模块连接wifi并将数据发送至阿里云平台。
具体代码在我资源里,大家可以下载参考。
我的开发环境是VScode+espidf4.4.4+python3.8.7.