You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#通过MODBUS RTU读取Renogy太阳能控制器无返回值问题排查

C#通过RS232读取Renogy太阳能控制器Modbus数据无返回问题排查

我正在尝试通过RS232协议读取支持MODBUS协议的Renogy Wanderer/Rover 20A/40A太阳能充电控制器的数据。

MODBUS读寄存器请求格式

描述字节数取值说明
设备地址BYTE01H 到 F7H
功能码BYTE03H
起始地址WORD0000H 到 FFFFH
读寄存器数量WORD0001H 到 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硬件接线故障导致,还是代码实现存在问题?


问题排查答案

代码和硬件都有可能出问题,按以下优先级排查:

先改代码的已知错误

  1. 接收逻辑完全错误:你用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("-", " "));
}
  1. 核对CRC计算结果:先打印代码生成的完整发送帧,确认是不是和预期的01 03 00 0A 00 01 A4 08完全一致,如果CRC结果不对,直接写死这个测试帧发送,先排除CRC计算的问题。
  2. 确认串口参数:部分Renogy控制器默认波特率是2400,不是9600,同时确认控制器的从站地址是不是默认的1,有没有手动修改过。

硬件排查步骤

  1. 先做串口环回测试:把USB转RS232的TX和RX短接,运行代码发送数据,看能不能收到自己发的帧,如果收不到就是COM口选错、驱动异常或者USB转串口硬件损坏。
  2. 核对接线:RS232的TX接控制器的RX,RX接控制器的TX,GND必须共地,80%的无返回问题都是TX/RX接反或者没接GND导致的。
  3. 确认控制器工作状态:控制器必须接电池或者太阳能板正常上电,才会响应Modbus请求。
  4. 用Modbus Poll等成熟调试工具测试:工具里配置对应参数,发送测试帧01 03 00 0A 00 01 A4 08,如果工具能收到返回就是代码问题,收不到就是硬件/接线问题。

内容的提问来源于stack exchange,提问作者Chuck O

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 11:24:03