如何在ESP32中使用mDNS(mDNS系列4)

起因

笔者的很多服务都启用了mDNS,因此在使用ESP32做一些小制作时,需要使用mDNS来获取服务地址,如mqtt.localinfluxdb.local等,故有了本文中的代码。

分析

mDNS组件在IDF v5.0大版本中被移除出默认的内部组件库中(详见参考1),因此需要修改之前的基础库代码,手动添加对应支持。在PlatformIO项目中,我将官方的源码下载到了本地,放在了third_party/mdns目录下,然后在最外层的CMakeLists.txt中添加了一行list(APPEND EXTRA_COMPONENT_DIRS third_party)来让编译系统知道这个目录下的第三方组件。接着,在看了官方的文档和源码后(详见参考2、参考3),对相关代码进行了封装,形成了自己的库文件,具体实现见下方代码。

实现

mdns.hpp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef _mdns_hpp_
#define _mdns_hpp_

#include <string>
#include <vector>

#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "mdns.h"

namespace cubestone_wang
{

namespace mdns
{

class MDNS
{
public:
// 日志标签
static const char *const LOG_TAG;
struct TxtItem {
std::string Key;
std::string Value;
};
struct Service{
std::string InstanceName;
std::string Type;
std::string Proto;
uint16_t Port;
std::vector<TxtItem> Txt;
};
static bool Start(const std::string &hostname,
const std::string &instance_name="",
const std::vector<Service> &services=std::vector<Service>());
static void Stop();
private:
static bool start_flag;
static SemaphoreHandle_t mutex;
};

}

}

#endif // _mdns_hpp_

mdns.cpp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "esp_log.h"

#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "mdns.h"
#include "event_loop.hpp"

#include "mdns.hpp"

namespace cubestone_wang
{

namespace mdns
{

using namespace event_loop;

const char *const MDNS::LOG_TAG = "MDNS";

bool MDNS::start_flag = false;

SemaphoreHandle_t MDNS::mutex = xSemaphoreCreateMutex();

bool MDNS::Start(const std::string &hostname,
const std::string &instance_name,
const std::vector<Service> &services)
{
// 判断是否已经启动
// 设置临界区
xSemaphoreTake(mutex, portMAX_DELAY);
if (start_flag == true) {
ESP_LOGI(LOG_TAG, "this has been started");
// 退出临界区
xSemaphoreGive(mutex);
return false;
}
// 在该类中初始化了事件循环
EventLoop::Init();
ESP_ERROR_CHECK(mdns_init());
ESP_ERROR_CHECK(mdns_hostname_set(hostname.c_str()));
if (0 < instance_name.length()) {
ESP_ERROR_CHECK(mdns_instance_name_set(instance_name.c_str()));
}
Service service;
for (uint32_t index=0; index<services.size(); index++) {
service = services[index];
ESP_ERROR_CHECK(mdns_service_add(NULL,
service.Type.c_str(),
service.Proto.c_str(),
service.Port,
NULL,
0));
for (uint32_t sub_index=0; sub_index<service.Txt.size(); sub_index++) {
ESP_ERROR_CHECK(mdns_service_txt_item_set(service.Type.c_str(),
service.Proto.c_str(),
service.Txt[sub_index].Key.c_str(),
service.Txt[sub_index].Key.c_str()));
}
if (0 < service.InstanceName.length()) {
ESP_ERROR_CHECK(mdns_service_instance_name_set(service.Type.c_str(),
service.Proto.c_str(),
service.InstanceName.c_str()));
}
}
start_flag = true;
// 退出临界区
xSemaphoreGive(mutex);
return true;
}

void MDNS::Stop()
{
xSemaphoreTake(mutex, portMAX_DELAY);
if (start_flag == false) {
ESP_LOGD(LOG_TAG, "this has been stopped");
goto DONE;
}
mdns_free();
DONE:
start_flag = false;
// 退出临界区
xSemaphoreGive(mutex);
return;
}

}

}

主流程中在WiFi连接成功后,即可按需调用如下代码初始化mDNS

1
2
3
4
5
6
7
8
9
auto mdns_config = (mdns::Config *)config::ConfigManager::Get(Application::mdns_config_name);
if(!mdns::MDNS::Start(mdns_config->Hostname,
mdns_config->InstanceName,
mdns_config->services)) {
std::string message = "mdns start failed";
reboot_for_failed_start(message);
return;
}
ESP_LOGI(LOG_TAG, "mdns start success");

参考

  1. IDF mDNS组件移除内部组件库说明
  2. IDF mDNS组件官方文档
  3. IDF mDNS组件源码