用ESP32-C3做个蓝牙中继器:把小米温湿度计的数据自动推送到Home Assistant(附完整Arduino代码)

张开发
2026/5/31 16:58:00 15 分钟阅读
用ESP32-C3做个蓝牙中继器:把小米温湿度计的数据自动推送到Home Assistant(附完整Arduino代码)
用ESP32-C3构建小米温湿度计数据中继器从BLE到Home Assistant的完整实践智能家居爱好者常遇到一个痛点优秀的蓝牙设备如小米温湿度计无法直接接入本地Home Assistant系统。本文将手把手教你用成本不到30元的ESP32-C3开发板搭建一个能自动读取小米蓝牙温湿度计数据并通过MQTT推送到Home Assistant的智能中继器。不同于通用教程我们特别针对小米设备的广播协议进行逆向解析并提供开箱即用的自动化配置方案。1. 硬件准备与环境搭建选择ESP32-C3而非传统ESP32的原因在于其RISC-V架构带来的性价比优势同时完全兼容Arduino生态。你需要准备以下硬件ESP32-C3开发板推荐型号Seeed Studio XIAO ESP32C3小米米家蓝牙温湿度计2型号LYWSD03MMCMicro USB数据线可选3D打印外壳或防水盒开发环境配置步骤安装Arduino IDE 2.0或VS CodePlatformIO组合添加ESP32开发板支持在首选项中添加https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json安装必要库文件BluetoothLE库BLEDeviceWiFi和MQTT库WiFiClientSecure、PubSubClient加密库libsodium用于小米数据解密// 基础库引用检查 #include Arduino.h #include BLEDevice.h #include WiFi.h #include PubSubClient.h2. 小米蓝牙协议逆向与数据解析小米温湿度计采用BLE广播方式传输数据其协议有以下几个关键特点广播间隔约10秒数据格式加密的16字节Manufacturer Specific Data需要绑定密钥才能解密可通过Xiaomi Cloud或第三方工具获取数据解密算法实现关键部分struct XiaomiData { float temperature; float humidity; uint8_t battery; }; XiaomiData decryptMiBeacon(uint8_t* encryptedData, uint8_t* bindKey) { XiaomiData result; // 解密算法实现 mbedtls_aes_context aes; mbedtls_aes_init(aes); mbedtls_aes_setkey_dec(aes, bindKey, 128); mbedtls_aes_crypt_ecb(aes, MBEDTLS_AES_DECRYPT, encryptedData, decryptedData); // 数据解析 result.temperature ((decryptedData[0] 8) | decryptedData[1]) / 10.0; result.humidity decryptedData[2]; result.battery decryptedData[3]; return result; }注意获取bindKey需用户自行从米家APP导出本文不涉及具体获取方法3. ESP32-C3蓝牙扫描优化策略针对小米设备的广播特性我们需要优化扫描策略以提高能效采用被动扫描模式减少功耗设置10秒的扫描窗口匹配设备广播周期添加MAC地址白名单过滤避免处理无关设备BLEScan* initBLEScan() { BLEDevice::init(ESP32C3-Gateway); BLEScan* pScanner BLEDevice::getScan(); pScanner-setActiveScan(false); // 被动扫描 pScanner-setInterval(100); // 扫描间隔100ms pScanner-setWindow(99); // 扫描窗口99ms pScanner-setDuplicateFilter(true); return pScanner; } bool isXiaomiDevice(BLEAdvertisedDevice device) { return device.getName() MJ_HT_V1 || device.getManufacturerData().find(FE95) ! -1; }4. MQTT集成与Home Assistant自动发现实现与Home Assistant无缝集成的关键是通过MQTT自动发现协议。我们创建以下主题结构homeassistant/sensor/xiaomi_lywsd03mmc_[MAC]/config homeassistant/sensor/xiaomi_lywsd03mmc_[MAC]/state自动发现消息示例JSON格式{ name: Xiaomi Temp, device_class: temperature, state_topic: xiaomi/[MAC]/state, unit_of_measurement: °C, value_template: {{ value_json.temperature}}, unique_id: lywsd03mmc_temp_[MAC], device: { identifiers: [[MAC]], name: Xiaomi LYWSD03MMC, manufacturer: Xiaomi, model: LYWSD03MMC } }完整MQTT发布函数void publishToHA(XiaomiData data, String mac) { DynamicJsonDocument doc(512); doc[temperature] data.temperature; doc[humidity] data.humidity; doc[battery] data.battery; String payload; serializeJson(doc, payload); client.publish((xiaomi/ mac /state).c_str(), payload.c_str()); }5. 电源管理与OTA升级为打造可长期运行的稳定中继器需特别注意低功耗配置方案模式电流消耗适用场景深度睡眠50μA电池供电轻度睡眠5mA持续监测全速运行80mA固件更新void enterLightSleep() { esp_sleep_enable_timer_wakeup(10 * 1000000); // 10秒后唤醒 esp_light_sleep_start(); }OTA升级实现步骤在Arduino IDE中启用网络端口选项添加基础OTA代码#include ArduinoOTA.h void setupOTA() { ArduinoOTA .onStart([]() { Serial.println(OTA Start); }) .onEnd([]() { Serial.println(OTA End); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf(Progress: %u%%\r, (progress / (total / 100))); }); ArduinoOTA.begin(); }6. 实战调试技巧与常见问题在三个月实际部署中我们总结了这些宝贵经验信号干扰问题当ESP32与小米设备距离超过3米时尝试调整ESP32天线方向避免将设备放置在金属表面使用5V独立电源而非USB供电数据抖动处理添加简单滤波算法float applyEMA(float newValue, float previous) { const float alpha 0.2; // 平滑系数 return alpha * newValue (1 - alpha) * previous; }MQTT断连恢复增强重连逻辑void checkMQTT() { if (!client.connected()) { int retries 0; while (!client.connected() retries 5) { if (client.connect(ESP32C3Client, MQTT_USER, MQTT_PASS)) { client.subscribe(homeassistant/status); } delay(1000 * pow(2, retries)); // 指数退避 retries; } } }这个项目最令人满意的部分是看到Home Assistant中实时显示小米温湿度计数据的那一刻整个过程没有使用任何云服务全部数据都在本地网络中安全传输。ESP32-C3的表现超出预期连续运行两周平均电流仅12mA配合10000mAh的充电宝可以工作超过一个月。

更多文章