C#通过MODBUS RTU读取Renogy太阳能控制器无返回值问题排查
C#通过RS232读取Renogy太阳能控制器Modbus数据无返回问题排查
我正在尝试通过RS232协议读取支持MODBUS协议的Renogy Wanderer/Rover 20A/40A太阳能充电控制器的数据。
MODBUS读寄存器请求格式
| 描述 | 字节数 | 取值说明 |
|---|---|---|
| 设备地址 | BYTE | 01H 到 F7H |
| 功能码 | BYTE | 03H |
| 起始地址 | WORD | 0000H 到 FFFFH |
| 读寄存器数量 | WORD | 0001H 到 007DH |
| 校验码 | WORD | 上述所有字节的CRC校验和 |
读取控制器系统电压、系统电流的十六进制请求示例(对应PDU地址为000AH):
发送帧: 01 03 000A 0001 A408 接收帧: 01 03 02 181E 324C
初始代码问题
最开始编写的C#代码如下:
using System; using System.IO.Ports; public class ModbusRTUProtocol { public static void Main() { SerialPort serialPort1 = null; try { Console.WriteLine("This is the beginning: "); serialPort1 = new SerialPort("COM2", 9600, Parity.None, 8, StopBits.One); if (serialPort1.IsOpen) serialPort1.Close(); serialPort1.DataReceived += Port_DataReceived; serialPort1.Open(); byte[] bytestosend = new byte[] { 0x01, 0x03, 0x000A, 0x0001, 0xA408 }; serialPort1.Write(bytestosend, 0, bytestosend.Length); } catch (Exception ex) { throw ex; } Console.ReadLine(); } private static void Port_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort port = sender as SerialPort; // read input string incoming = port.ReadExisting(); Console.WriteLine(incoming); } }
运行时报错CS0031:常量值'41992'无法转换为'byte',原因是CRC校验值A408为双字节数据,超出了byte类型的取值范围。
代码更新后仍无返回
参考建议后将所有多字节字段拆分为单字节组帧,发送帧为{0x01, 0x03, 0x00, 0x0A, 0x00, 0x01, 0xA4, 0x08},修改后的代码如下:
using System; using System.IO.Ports; public class ModbusRTUProtocol { public static void Main() { SerialPort serialPort1 = null; try { Console.WriteLine("This is the beginning: "); byte slaveAddress = 1; byte function = 3; string hex_add = "000A"; ushort startAddress = Convert.ToUInt16(hex_add, 16); uint numberOfPoints = 1; Console.WriteLine("Value of hex: " + hex_add); Console.WriteLine("Value of ushort: " + startAddress); serialPort1 = new SerialPort("COM2", 9600, Parity.None, 8, StopBits.One); if (serialPort1.IsOpen) serialPort1.Close(); serialPort1.DataReceived += Port_DataReceived; serialPort1.Open(); byte[] frame = ReadHoldingRegistersMsg(slaveAddress, startAddress, function, numberOfPoints); foreach(var item in frame) { Console.Write(string.Format("{0:X2} ", item)); } serialPort1.Write(frame, 0, frame.Length); } catch (Exception ex) { throw ex; } Console.ReadLine(); } private static void Port_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort port = sender as SerialPort; // read input string incoming = port.ReadExisting(); Console.WriteLine(incoming); } private static byte[] ReadHoldingRegistersMsg(byte slaveAddress, ushort startAddress, byte function, uint numberOfPoints) { byte[] frame = new byte[8]; frame[0] = slaveAddress; // Slave Address frame[1] = function; // Function frame[2] = (byte)(startAddress >> 8); // Starting Address High frame[3] = (byte)startAddress; // Starting Address Low frame[4] = (byte)(numberOfPoints >> 8); // Quantity of Registers High frame[5] = (byte)numberOfPoints; // Quantity of Registers Low byte[] crc = CalculateCRC(frame); // Calculate CRC. frame[6] = crc[0]; // Error Check Low frame[7] = crc[1]; // Error Check High return frame; } private static byte[] CalculateCRC(byte[] data) { ushort CRCFull = 0xFFFF; // Set the 16-bit register (CRC register) = FFFFH. byte CRCHigh = 0xFF, CRCLow = 0xFF; char CRCLSB; byte[] CRC = new byte[2]; for (int i = 0; i < (data.Length) - 2; i++) { CRCFull = (ushort)(CRCFull ^ data[i]); // for (int j = 0; j < 8; j++) { CRCLSB = (char)(CRCFull & 0x0001); CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF); if (CRCLSB == 1) CRCFull = (ushort)(CRCFull ^ 0xA001); } } CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF); CRC[0] = CRCLow = (byte)(CRCFull & 0xFF); return CRC; } }
现在发送上述帧后仍未收到控制器返回的数据,请问该问题是由USB转RS232硬件接线故障导致,还是代码实现存在问题?
问题排查答案
代码和硬件都有可能出问题,按以下优先级排查:
先改代码的已知错误
- 接收逻辑完全错误:你用
ReadExisting()把二进制Modbus帧按字符串读取,会把0x00等特殊字节当成字符串结束符,直接丢数据,也会出现乱码。把接收方法改成读字节数组:
private static void Port_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort port = sender as SerialPort; int bytesToRead = port.BytesToRead; byte[] buffer = new byte[bytesToRead]; port.Read(buffer, 0, bytesToRead); Console.WriteLine("收到数据:" + BitConverter.ToString(buffer).Replace("-", " ")); }
- 核对CRC计算结果:先打印代码生成的完整发送帧,确认是不是和预期的
01 03 00 0A 00 01 A4 08完全一致,如果CRC结果不对,直接写死这个测试帧发送,先排除CRC计算的问题。 - 确认串口参数:部分Renogy控制器默认波特率是2400,不是9600,同时确认控制器的从站地址是不是默认的1,有没有手动修改过。
硬件排查步骤
- 先做串口环回测试:把USB转RS232的TX和RX短接,运行代码发送数据,看能不能收到自己发的帧,如果收不到就是COM口选错、驱动异常或者USB转串口硬件损坏。
- 核对接线:RS232的TX接控制器的RX,RX接控制器的TX,GND必须共地,80%的无返回问题都是TX/RX接反或者没接GND导致的。
- 确认控制器工作状态:控制器必须接电池或者太阳能板正常上电,才会响应Modbus请求。
- 用Modbus Poll等成熟调试工具测试:工具里配置对应参数,发送测试帧
01 03 00 0A 00 01 A4 08,如果工具能收到返回就是代码问题,收不到就是硬件/接线问题。
内容的提问来源于stack exchange,提问作者Chuck O
相关产品推荐
相关产品推荐

