串行通信超时长电缆超时
Serial communication timeout on long cable time out
我有一个通过 rs232 读取不同硬件的应用程序。它已经过测试并且运行良好。对于最终应用,我需要引入几百米长的电缆,这意味着我有 rs485 转换器。
当我 运行 我的应用程序读取硬件时,我收到 System.IO.Ports.SerialStream.Read 的超时错误。不幸的是,我已将超时时间增加到 20 秒,但它并没有解决问题
我尝试了不同的应用程序来读取硬件,即使读取频率为 1 秒,它们也能正常工作。
通信使用的是 modbus 协议,在当前阶段我认为它是无关紧要的,因为我没有到达该阶段接收任何东西。
我的代码如下所示:
先打开串口并初始化:
//get the right modbus data structure element
ModBus MB = (ModBus)s[0].sensorData;
//set up the serial port regarding the data structure's data
SerialPort sp = new SerialPort();
sp.PortName = MB.portName;
sp.BaudRate = Convert.ToInt32(MB.baudRate);
sp.DataBits = MB.dataBits;
sp.Parity = MB.parity;
sp.StopBits = MB.stopBits;
//Set time outs 20 sec for now
sp.ReadTimeout = 20000;
sp.WriteTimeout = 20000;
//将端口添加到可以被reader访问的列表
portList.Add(sp);
sp.Open();
读取硬件:
//get the right port for com
SerialPort sp = getRightPort();
ModBus MB = getRightModBusStructureelement();
try
{
//Clear in/out buffers:
sp.DiscardOutBuffer();
sp.DiscardInBuffer();
//create modbus read message
byte[] message = createReadModBusMessage();
try
{
sp.Write(message, 0, message.Length);
// FM.writeErrorLog output included for easier debug
FM.writeErrorLog(DateTime.Now + ": ModBus Message Sent");
FM.writeErrorLog(DateTime.Now + ": Read TimeOut = " + sp.ReadTimeout + " Write TimeOut = " + sp.WriteTimeout);
int offset = 0, bytesRead;
int bytesExpected = response.Length;
FM.writeErrorLog(DateTime.Now + ": start read");
while (bytesExpected > 0 && (bytesRead = sp.Read(response, offset, bytesExpected)) > 0)
{
FM.writeErrorLog(DateTime.Now + ": read - " + offset);
offset += bytesRead;
bytesExpected -= bytesRead;
}
}
catch (Exception err)
{
Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err);
FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err);
}
}
尝试应用程序后,我从 ErroLog.txt 得到以下输出:
14/01/2016 17:18:17: ModBus Message Sent
14/01/2016 17:18:17: Read TimeOut = 20000 Write TimeOut = 20000
14/01/2016 17:18:18: start read
14/01/2016 17:18:38 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out.
at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout)
at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count)
at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count)
at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter)
14/01/2016 17:18:38: 0
14/01/2016 17:18:38: 0
我已将超时增加到 60 秒以防万一,但同样的错误:
15/01/2016 11:11:51: ModBus Message Sent
15/01/2016 11:11:51: Read TimeOut = 60000 Write TimeOut = 60000
15/01/2016 11:11:51: start read
15/01/2016 11:12:51 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out.
at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout)
at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count)
at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count)
at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter)
15/01/2016 11:12:51: 0
15/01/2016 11:12:51: 0
我尝试了几种不同的读取串口的方法,我认为目前的方法看起来最好,这是我读取代码中的while循环。
我没有包括我的代码的其余部分,因为它之前超时了,我认为它是无关紧要的。
假设这不是 hardware/long 电缆问题,您可以在代码中做一些事情来处理错误:
您需要像创建 getRightPort
:
一样创建一个 "right" 错误处理程序
SerialPort sp = getRightPort();
假设里面有Collection
个串口项,你return选对了,如果这个SerialPort
有错误,一定要重新创建错误 SerialPort
object
具有相同的设置:
catch (Exception err)
{
Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err);
FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err);
reinitRightPort(sp); //add this
}
方法 reinitRightPort();
可能看起来与您第一次启动的方式相似,有细微差别,例如:
- 您不再需要在
List<SerialPort>
中添加此内容
- 您不需要在方法中声明
SerialPort
,您可以从输入参数中获取它。
- 最好引入一些输入检查有效性以避免以后已知的错误
- 也许你可以关闭之前的连接,只是为了确保端口可以被新的
SerialPort
object
. 使用
像这样:
private void reinitRightPort(SerialPort sp){ //get SerialPort from outside of method
//Add whatever necessary here, to close all the current connections
//Plus all the error handlings
if (sp == null) //Read 3.
return;
sp.Close(); //Read 4. close the current connections
//get the right modbus data structure element
ModBus MB = (ModBus)s[0].sensorData;
//set up the serial port regarding the data structure's data
sp = new SerialPort(); //Read 2. sp is from outside the method, but use new keyword still
sp.PortName = MB.portName;
sp.BaudRate = Convert.ToInt32(MB.baudRate);
sp.DataBits = MB.dataBits;
sp.Parity = MB.parity;
sp.StopBits = MB.stopBits;
//Set time outs 20 sec for now
sp.ReadTimeout = 20000;
sp.WriteTimeout = 20000;
//portList.Add(sp); Read 1. no need to add this! It is already there!
sp.Open();
}
注意:在您这样做并确保您的端口运行良好之后,只要有可能,您还可以将上面的 reinitRightPort
方法与您的实际初始化结合起来,并进行一些小的修改。但是您可能想要做的第一件事是让您的串行端口在错误的情况下工作。
But if the error source comes from hardware/long cable issue (such as
cable placing in the RS232 or RS485, or voltage drop due to long
cable, or incompatible hardware: that is, in short, nothing to do with
coding at all), then, unfortunately the solution cannot come from code
as well. You got to find the real hardware issue.
如果您使用的是数百米长的串行电缆(这本身就是一个硬件工程问题),那么我强烈建议在电缆的两端都安装一个合适的收发器。电缆本身应该是 EMC 屏蔽的并且是高质量的。长距离非屏蔽电缆可能会受到感应电压波动的影响,这可能会损坏并非设计用于处理长距离电缆的设备。
即使使用良好的电缆,您仍然会有相当大的电压降和电感/电容效应,这可能会阻止以更高的波特率进行通信。 运行 以您可以摆脱的最低波特率。
假设这是一个硬件问题(我猜是,我也必须解决类似的问题),我建议用旁边的设备服务器(通过以太网)替换数百米的串行电缆串行设备。设备服务器可以模拟您 PC 上的 COM 端口,因此可以缩短串行电缆。
不幸的是,这些服务器比几米的电缆要贵一点..
请注意RS232和RS485的区别在于RS232是全双工的而RS485只是半双工的。由于 modbus 是一种请求响应类型的协议,这不是您的问题,但了解这一点很重要。
因此,RS232<->RS485 必须知道何时打开它的 RS485 发射器。这可以在不同的转换器上以不同的方式完成,也可以是可配置的。这可以通过 RS232 具有但 RS485 没有的附加控制线来完成。 RTS/CTS。
如果配置错误,响应方(等待请求响应)的发射器可能会打开,然后它无法接收任何东西。
手册中的示例是 http://ftc.beijer.se/files/C125728B003AF839/992C59EC02C66E00C12579C60051484E/westermo_ug_6617-2203_mdw-45.pdf
这是瑞典流行的型号,具有三种操作模式。它可以仅通过传入数据打开 on/off 发射器,由 DB9-RS232 连接器上的 RTS 引脚控制,或者让发射器始终开启(对于每个方向都有单独电线的 RS422)
http://screencast.com/t/KrkEw13J8
这很难,因为有些制造商不会打印出它的实际工作原理。分不清DCE和DTE也极易接线错误
正如我在问题中提到的那样,我能够使用其他软件读取硬件,因此这一定是软件错误。在调查了我可以在串口设置上操作的所有可能变量后,我想到了关闭握手并让它始终被接受的想法。
稍微深入一下,我得到了以下代码,增加了写入和读取超时。它解决了我的问题:
sp.ReadTimeout = 60000;
sp.WriteTimeout = 60000;
sp.DtrEnable = true;
sp.RtsEnable = true;
sp.Handshake = Handshake.None;
希望以后对其他人有所帮助,感谢大家的帮助和努力。
我有一个通过 rs232 读取不同硬件的应用程序。它已经过测试并且运行良好。对于最终应用,我需要引入几百米长的电缆,这意味着我有 rs485 转换器。
当我 运行 我的应用程序读取硬件时,我收到 System.IO.Ports.SerialStream.Read 的超时错误。不幸的是,我已将超时时间增加到 20 秒,但它并没有解决问题
我尝试了不同的应用程序来读取硬件,即使读取频率为 1 秒,它们也能正常工作。
通信使用的是 modbus 协议,在当前阶段我认为它是无关紧要的,因为我没有到达该阶段接收任何东西。
我的代码如下所示: 先打开串口并初始化:
//get the right modbus data structure element
ModBus MB = (ModBus)s[0].sensorData;
//set up the serial port regarding the data structure's data
SerialPort sp = new SerialPort();
sp.PortName = MB.portName;
sp.BaudRate = Convert.ToInt32(MB.baudRate);
sp.DataBits = MB.dataBits;
sp.Parity = MB.parity;
sp.StopBits = MB.stopBits;
//Set time outs 20 sec for now
sp.ReadTimeout = 20000;
sp.WriteTimeout = 20000;
//将端口添加到可以被reader访问的列表 portList.Add(sp); sp.Open();
读取硬件:
//get the right port for com
SerialPort sp = getRightPort();
ModBus MB = getRightModBusStructureelement();
try
{
//Clear in/out buffers:
sp.DiscardOutBuffer();
sp.DiscardInBuffer();
//create modbus read message
byte[] message = createReadModBusMessage();
try
{
sp.Write(message, 0, message.Length);
// FM.writeErrorLog output included for easier debug
FM.writeErrorLog(DateTime.Now + ": ModBus Message Sent");
FM.writeErrorLog(DateTime.Now + ": Read TimeOut = " + sp.ReadTimeout + " Write TimeOut = " + sp.WriteTimeout);
int offset = 0, bytesRead;
int bytesExpected = response.Length;
FM.writeErrorLog(DateTime.Now + ": start read");
while (bytesExpected > 0 && (bytesRead = sp.Read(response, offset, bytesExpected)) > 0)
{
FM.writeErrorLog(DateTime.Now + ": read - " + offset);
offset += bytesRead;
bytesExpected -= bytesRead;
}
}
catch (Exception err)
{
Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err);
FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err);
}
}
尝试应用程序后,我从 ErroLog.txt 得到以下输出:
14/01/2016 17:18:17: ModBus Message Sent
14/01/2016 17:18:17: Read TimeOut = 20000 Write TimeOut = 20000
14/01/2016 17:18:18: start read
14/01/2016 17:18:38 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out.
at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout)
at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count)
at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count)
at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter)
14/01/2016 17:18:38: 0
14/01/2016 17:18:38: 0
我已将超时增加到 60 秒以防万一,但同样的错误:
15/01/2016 11:11:51: ModBus Message Sent
15/01/2016 11:11:51: Read TimeOut = 60000 Write TimeOut = 60000
15/01/2016 11:11:51: start read
15/01/2016 11:12:51 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out.
at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout)
at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count)
at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count)
at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter)
15/01/2016 11:12:51: 0
15/01/2016 11:12:51: 0
我尝试了几种不同的读取串口的方法,我认为目前的方法看起来最好,这是我读取代码中的while循环。
我没有包括我的代码的其余部分,因为它之前超时了,我认为它是无关紧要的。
假设这不是 hardware/long 电缆问题,您可以在代码中做一些事情来处理错误:
您需要像创建 getRightPort
:
SerialPort sp = getRightPort();
假设里面有Collection
个串口项,你return选对了,如果这个SerialPort
有错误,一定要重新创建错误 SerialPort
object
具有相同的设置:
catch (Exception err)
{
Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err);
FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err);
reinitRightPort(sp); //add this
}
方法 reinitRightPort();
可能看起来与您第一次启动的方式相似,有细微差别,例如:
- 您不再需要在
List<SerialPort>
中添加此内容 - 您不需要在方法中声明
SerialPort
,您可以从输入参数中获取它。 - 最好引入一些输入检查有效性以避免以后已知的错误
- 也许你可以关闭之前的连接,只是为了确保端口可以被新的
SerialPort
object
. 使用
像这样:
private void reinitRightPort(SerialPort sp){ //get SerialPort from outside of method
//Add whatever necessary here, to close all the current connections
//Plus all the error handlings
if (sp == null) //Read 3.
return;
sp.Close(); //Read 4. close the current connections
//get the right modbus data structure element
ModBus MB = (ModBus)s[0].sensorData;
//set up the serial port regarding the data structure's data
sp = new SerialPort(); //Read 2. sp is from outside the method, but use new keyword still
sp.PortName = MB.portName;
sp.BaudRate = Convert.ToInt32(MB.baudRate);
sp.DataBits = MB.dataBits;
sp.Parity = MB.parity;
sp.StopBits = MB.stopBits;
//Set time outs 20 sec for now
sp.ReadTimeout = 20000;
sp.WriteTimeout = 20000;
//portList.Add(sp); Read 1. no need to add this! It is already there!
sp.Open();
}
注意:在您这样做并确保您的端口运行良好之后,只要有可能,您还可以将上面的 reinitRightPort
方法与您的实际初始化结合起来,并进行一些小的修改。但是您可能想要做的第一件事是让您的串行端口在错误的情况下工作。
But if the error source comes from hardware/long cable issue (such as cable placing in the RS232 or RS485, or voltage drop due to long cable, or incompatible hardware: that is, in short, nothing to do with coding at all), then, unfortunately the solution cannot come from code as well. You got to find the real hardware issue.
如果您使用的是数百米长的串行电缆(这本身就是一个硬件工程问题),那么我强烈建议在电缆的两端都安装一个合适的收发器。电缆本身应该是 EMC 屏蔽的并且是高质量的。长距离非屏蔽电缆可能会受到感应电压波动的影响,这可能会损坏并非设计用于处理长距离电缆的设备。
即使使用良好的电缆,您仍然会有相当大的电压降和电感/电容效应,这可能会阻止以更高的波特率进行通信。 运行 以您可以摆脱的最低波特率。
假设这是一个硬件问题(我猜是,我也必须解决类似的问题),我建议用旁边的设备服务器(通过以太网)替换数百米的串行电缆串行设备。设备服务器可以模拟您 PC 上的 COM 端口,因此可以缩短串行电缆。
不幸的是,这些服务器比几米的电缆要贵一点..
请注意RS232和RS485的区别在于RS232是全双工的而RS485只是半双工的。由于 modbus 是一种请求响应类型的协议,这不是您的问题,但了解这一点很重要。
因此,RS232<->RS485 必须知道何时打开它的 RS485 发射器。这可以在不同的转换器上以不同的方式完成,也可以是可配置的。这可以通过 RS232 具有但 RS485 没有的附加控制线来完成。 RTS/CTS。 如果配置错误,响应方(等待请求响应)的发射器可能会打开,然后它无法接收任何东西。
手册中的示例是 http://ftc.beijer.se/files/C125728B003AF839/992C59EC02C66E00C12579C60051484E/westermo_ug_6617-2203_mdw-45.pdf 这是瑞典流行的型号,具有三种操作模式。它可以仅通过传入数据打开 on/off 发射器,由 DB9-RS232 连接器上的 RTS 引脚控制,或者让发射器始终开启(对于每个方向都有单独电线的 RS422) http://screencast.com/t/KrkEw13J8
这很难,因为有些制造商不会打印出它的实际工作原理。分不清DCE和DTE也极易接线错误
正如我在问题中提到的那样,我能够使用其他软件读取硬件,因此这一定是软件错误。在调查了我可以在串口设置上操作的所有可能变量后,我想到了关闭握手并让它始终被接受的想法。
稍微深入一下,我得到了以下代码,增加了写入和读取超时。它解决了我的问题:
sp.ReadTimeout = 60000;
sp.WriteTimeout = 60000;
sp.DtrEnable = true;
sp.RtsEnable = true;
sp.Handshake = Handshake.None;
希望以后对其他人有所帮助,感谢大家的帮助和努力。