项目背景
2022 年的时候,我在做一个智能家居相关的项目,需要在 ESP32 上使用 MQTT 协议与后端通信。当时找了一圈现有的 Arduino 库,发现存在几个问题:
- 很多库没有适配 ESP32 Arduino Core 3.x 版本
- 部分库在多线程环境下不稳定,会出现崩溃
- 有些库虽然功能完整,但内存占用过高
于是我决定基于 ESP-IDF 原生的 MQTT 组件,封装一个简单易用的 Arduino 库。
技术选型
ESP-IDF 从 4.x 升级到 5.x 后,MQTT 客户端 API 有了较大变化。我对比了几种方案:
| 方案 | 优点 | 缺点 |
|---|---|---|
| PubSubClient | 轻量、广泛使用 | 未适配 ESP32 Core 3.x,功能较简单 |
| AsyncMqttClient | 异步非阻塞 | 依赖旧版 AsyncTCP,维护停滞 |
| esp-mqtt (IDF) | 官方维护,功能完整 | API 较底层,需要封装 |
最终选择了基于 esp-mqtt 进行封装,这样既能保证功能完整,又能提供简洁的 Arduino 风格 API。
线程安全的设计
ESP32 是双核处理器,经常在 FreeRTOS 的多任务环境下使用。原生的 esp-mqtt 虽然是线程安全的,但在 Arduino 层面的封装需要考虑更多问题。
互斥锁的使用
对于可能被多个任务同时调用的方法,我添加了 FreeRTOS 互斥锁保护:
bool ESP32MQTTClient::publish(const char* topic, const char* payload, uint8_t qos, bool retain) {
if (_mutex == NULL) return false;
// 获取互斥锁,最多等待 1 秒
if (xSemaphoreTake(_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
log_w("Failed to take mutex");
return false;
}
esp_mqtt_client_publish(_client, topic, payload, 0, qos, retain);
xSemaphoreGive(_mutex);
return true;
}
回调函数的上下文
MQTT 事件回调在中断上下文中执行,不能直接调用阻塞操作。我将事件放入队列,在 loop() 中统一处理:
void ESP32MQTTClient::loop() {
MQTTEvent event;
while (xQueueReceive(_eventQueue, &event, 0) == pdTRUE) {
switch (event.type) {
case MQTT_EVENT_CONNECTED:
if (_onConnect) _onConnect();
break;
case MQTT_EVENT_DATA:
if (_onMessage) {
_onMessage(event.topic, event.payload);
}
break;
// ...
}
}
}
适配 ESP-IDF 5.x
ESP-IDF 5.x 相对于 4.x 有一些破坏性变更,主要集中在:
- 头文件路径变更:
mqtt_client.h的位置从esp-mqtt/mqtt_client.h改为mqtt_client.h - 配置结构体变化:部分字段被移除或重命名
- 证书处理:TLS 相关 API 有调整
我通过条件编译来兼容两个版本:
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#include "mqtt_client.h"
#else
#include "esp-mqtt/mqtt_client.h"
#endif
使用示例
最终封装后的使用方式非常简单:
#include
ESP32MQTTClient mqtt;
void setup() {
mqtt.setURI("mqtt://broker.hivemq.com:1883");
mqtt.setOnMessageCallback([](String topic, String payload) {
Serial.println("Received: " + topic + " = " + payload);
});
mqtt.begin();
}
void loop() {
mqtt.loop();
mqtt.publish("test/topic", "hello");
delay(1000);
}
社区反馈
开源后陆续收到了一些 Issue 和 PR,主要关注点:
- 稳定性:用户反馈在长时间运行场景下表现良好
- 内存占用:相比其他库,RAM 占用控制在合理范围
- 兼容性:成功运行在 ESP32-C3、S2、S3 等新芯片上
总结
这个项目让我学到了很多关于 ESP32 和 MQTT 的知识,特别是多线程编程和跨版本兼容性处理。目前该库已经获得 40+ Star,被用于多个实际项目中。