【建筑施工监测与安防系统】十一、Kaluga MQTT连接OneNET
[复制链接]
参考ESP32 IDF的案例“mqtt/tcp”(..\esp-idf-v4.4\examples\protocols\mqtt\tcp)引入MQTT Client功能,使得Kaluga板可以连接OneNET(本人采用MQTT旧版,非加密)。
1、官方mqtt/tcp案例说明
IDF中有关联网及协议应用的案例大多依靠另一个联网案例实现Wi-Fi接入,这个案例就是“..\esp-idf-v4.4\examples\common_components\protocol_examples_common”。所以在移植MQTT案例到自己的项目时,要注意自己做好Wi-Fi接入。
图11-1 mqtt/tcp案例的主要函数
2、mqtt移植到自己项目
这里不介绍如何实现Wi-Fi接入了,不清楚的朋友可以去看本人的第七篇帖子。将mqtt/tcp的源码拷贝到自己的项目中,当然相关函数都要进行修改。首先是原案例需要导入的头文件:
- #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"
然后就是回调函数,下面代码本人加了注释的两个地方,是在下觉得需要关注的点,一个是断联后原案例只是给个控制台输出,这里加上重连函数调用。另一个是接收到OneNET发送命令的处理分支,后续可以在这里加入命令解析和处理代码。
-
- void log_error_if_nonzero(const char *message, int error_code) {
- if(error_code != 0) {
- ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
- }
- }
-
- 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");
- 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);
-
- msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
- ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
-
- msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
- ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
-
- 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:
- ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
- esp_mqtt_client_reconnect(client);
- break;
-
- case MQTT_EVENT_SUBSCRIBED:
- 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");
- if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
- log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
- log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
- log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
- ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
- }
- break;
- default:
- ESP_LOGI(TAG, "Other event id:%d", event->event_id);
- break;
- }
- }
然后是原案例的接入函数mqtt_app_start()这里做了修改——原例也没法直接用,缺少必要的配置(如:client_id,username等)。MQTT相关操作(连接、发布等)都需要一个“句柄”(esp_mqtt_client_handle_t类型),原案例是定义在函数中的局部量,因为事件回调可以得到这个句柄变量的传参所以无所谓。但是要单独编写发布数据包函数,就需要将句柄定义为全局量了。
- esp_mqtt_client_handle_t client;
- void aita_StartMqtt(void) {
- esp_mqtt_client_config_t mqtt_cfg = {
- .host = "183.230.40.39",
- .port = 6002,
- .client_id = "deviceid",
- .username = "userid",
- .password = "authorization",
- };
-
- client = esp_mqtt_client_init(&mqtt_cfg);
-
- esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
- esp_mqtt_client_start(client);
- }
再编写一个发送函数,注意data也就是发布数据包的载荷(payload)需要按照OneNET平台的格式要求就行封装,不清楚的朋友可以查看本人的ESP32 C3测评帖子。
- void aita_PubMqtt(const char *data, int len) {
- esp_mqtt_client_publish(client, "$dp", data, len, 0, 0);
- }
最后,结合本人的上篇帖子,在定时器回调中调用aita_PubMqtt(),暂时还是功能测试,就将定时器的启用时间发送到OneNET平台上。
- static void periodic_timer_callback(void* arg) {
- int64_t time_since_boot = esp_timer_get_time();
- char dat[80];
- sprintf(dat, "{\"tick\":%d}", (int)time_since_boot);
- char buf[83];
- int len = strlen(dat);
- buf[0] = 0x03;
- buf[1] = len>>8;
- buf[2] = len&0xff;
- memcpy(buf+3, dat, len);
- buf[3+len] = 0;
- aita_PubMqtt(buf, 3+len);
-
-
-
- }
图11-2 OneNET收到数据的展示
|