1.MQTT协议
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅
(publish/subscribe
)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。
MQTT最大优点在于,用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。
作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。
MQTT协议特点
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。
MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。
简单介绍一下MQTT协议,关于具体的报文读者可以自行了解,本文在于应用。
2.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把数据上传至阿里云。
3.设置WIFI
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.