为小米 IoT 模块编译自定义固件 (ESP32)

使用小米模组的第三方智能设备需要接入米家才能使用,通过编译和刷入自定义固件,可以实现不依赖小米云服务器的纯本地控制。

固件编写

这类设备的硬件架构通常是小米 IoT 模块和主控 MCU 通过一组 UART 进行通信,在 小米 IoT 开发者平台 里面可以查到具体协议。因此自定义固件只需要不断读取并解析串口内容,并根据通信协议为 MCU 的指令生成响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <HardwareSerial.h>

HardwareSerial SerialPort(2); // use UART2

void setup() {
SerialPort.begin(115200, SERIAL_8N1, 16, 17); // GPIO 16,17
}

void loop() {
delay(200);
char lineBuffer[64];
while(SerialPort.available()) {
int length = SerialPort.readBytesUntil('\r', lineBuffer, sizeof(lineBuffer) - 1);
lineBuffer[length] = 0;
if (!strncmp(lineBuffer, "model", 5))
HandleModel(...);
else if (!strncmp(lineBuffer, "mcu_version", 11))
SerialPort.print("ok\r"); // do nothing
else if (...)
HandleXX(...);
else
SerialPort.print("error\r");
}
}

编译和刷入

小米 ESP32 模块和一般的 ESP32-WROOM-32D 模块不一样,其中的 Core1 被屏蔽了,只能单核运行;另外 efuse blk0 的校验和也是不正确的,运行原版 esp-idf 会报错。因此需要使用 patch 过的 SDK 来编译 (https://github.com/mmakaay/arduino-esp32-unicore-no-mac-crc/tree/main)

刷入固件的过程和普通的 ESP32 相同,拉低 GPIO0 进入串口刷机模式,用 esptool 即可实现烧录。在刷之前可以先备份原厂固件:esptool --port /dev/ttyUSBx --baud 512000 read_flash 0 0x400000 backup.bin,同时保存一下原厂固件的串口输出。

通过 IoT 模块控制设备

在 miot 中,对设备的控制是通过 set_properties 和 get_properties 命令实现的。根据设备型号去 https://home.miot-spec.com/spec/{model} 查询支持的属性及其对应的 SIID 和 PIID,利用这些信息就可以实现对设备的控制。