ESP32读取Makel M600电表数据出现串口乱码问题求助
问题分析与解决方案
你的核心问题集中在RS485收发控制缺失、IEC 62056-21协议指令格式错误、初始波特率不匹配这几个关键点,以下是针对性的解决步骤:
1. 必须添加RS485收发控制引脚
RS485是半双工通信,必须通过DE/RE引脚切换收发状态:
- 发送数据时,将DE/RE置为高电平
- 发送完成后立即置为低电平,才能接收电表返回的数据
修改步骤:
- 选择ESP32的一个GPIO作为DE/RE控制引脚(例:GPIO4)
- 在代码中初始化该引脚为输出模式
- 发送指令前后切换引脚状态
2. 修正IEC 62056-21协议的指令格式
你的代码中发送的是字符串字面量(如<CR><LF>、[SOH]),这些不是协议要求的控制字符,必须替换为对应ASCII控制码:
<CR>对应\r(ASCII 0x0D),<LF>对应\n(ASCII 0x0A)SOH为0x01,STX为0x02,ETX为0x03- 初始识别指令应为
/?!\r\n,而非字符串"/?!<CR><LF>" - BCC校验位需计算:从STX到ETX所有字节的异或值,不能直接写
[BCC]
3. 匹配电表初始波特率
Makel M600电表的IEC 62056-21初始波特率默认是300bps(协议规定的Baud rate A),你一开始用9600bps会直接导致通信乱码,必须先以300bps完成识别,再协商切换到更高波特率。
4. 修正代码中的其他问题
- 移除重复的
Serial.begin(9600)调用 - 优先使用ESP32的硬件串口(HardwareSerial),比SoftwareSerial更稳定
修改后的示例代码
#include <WiFi.h> // 定义RS485控制引脚 #define RS485_DE_RE 4 // 使用硬件串口2(GPIO16=RX, GPIO17=TX) HardwareSerial rs485Serial(2); const char* ssid = "--"; const char* password = "--"; void setup() { Serial.begin(9600); // 初始化RS485控制引脚 pinMode(RS485_DE_RE, OUTPUT); digitalWrite(RS485_DE_RE, LOW); // 默认处于接收状态 // 连接WiFi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); // 初始化RS485串口,初始波特率300bps(IEC 62056-21默认) rs485Serial.begin(300, SERIAL_8N1); delay(1000); sendIdentificationCommand(); } void loop() { if (rs485Serial.available()) { String response = rs485Serial.readStringUntil('\n'); Serial.println("Received: " + response); // 识别响应格式为 /METER_ADDRESS\r\n,比如/SAT... if (response.startsWith("/")) { // 协商切换到9600bps(协议帧格式:050代表9600) sendBaudRateChangeCommand(); // 切换本地串口波特率到9600 rs485Serial.end(); rs485Serial.begin(9600, SERIAL_8N1); delay(500); // 发送读取有功电能的指令 sendReadEnergyCommand(); } } } // 发送设备识别指令 /?!\r\n void sendIdentificationCommand() { digitalWrite(RS485_DE_RE, HIGH); rs485Serial.print("/?!\r\n"); rs485Serial.flush(); // 等待发送完成 digitalWrite(RS485_DE_RE, LOW); } // 发送波特率切换指令 [DATA]050\r\n void sendBaudRateChangeCommand() { digitalWrite(RS485_DE_RE, HIGH); rs485Serial.print("\x10\x02050\x03\x1B\r\n"); // DATA对应ASCII 0x10 rs485Serial.flush(); digitalWrite(RS485_DE_RE, LOW); } // 发送读取有功电能(1.8.0)的指令,带BCC校验 void sendReadEnergyCommand() { digitalWrite(RS485_DE_RE, HIGH); // 构造指令:SOH(0x01) + R2 + STX(0x02) + 1.8.0 + ETX(0x03) + BCC byte cmd[] = {0x01, 'R', '2', 0x02, '1', '.', '8', '.', '0', 0x03}; // 计算BCC:从STX到ETX的异或 byte bcc = 0; for (int i=3; i<sizeof(cmd); i++) { bcc ^= cmd[i]; } // 发送指令 rs485Serial.write(cmd, sizeof(cmd)); rs485Serial.write(bcc); rs485Serial.print("\r\n"); rs485Serial.flush(); digitalWrite(RS485_DE_RE, LOW); }
额外调试建议
- 用示波器或逻辑分析仪检测RS485总线波形,确认发送的指令是否正确
- 检查RS485模块接线:A接A,B接B,必须共地(无共地极易出现乱码)
- 确保RS485模块工作电压与ESP32匹配(通常为3.3V或5V)
内容的提问来源于stack exchange,提问作者Nur Guzay
相关产品推荐
相关产品推荐

