2

I'm trying to get bidirectional communication using RS485 2 wires and everything I've tried so far failed.

I can send data and have my peripheral react as expected (so the wiring is correct), but I never receive any reply.


I'm using the .NET SerialPort, I've tried using the DataReceived event and also a loop in a Thread polling the port. I've even tried just blocking on read until enough data is received.


I have tried several hardware options:

  • PCI RS232 Card with a Sena RS232-RS485 converter
  • PCI RS232 Card with a Moxa RS232-RS485 converter
  • PCI RS485 Card

I have played with driver settings:

  • FIFO Interrupt Trigger levels
  • Receiver FIFO Flow Control Thresholds
  • RS485 Buffer enable (Normal, Active High, Active Low)

Following various leads (like Can't receive serial data in .net 2.0, using rs232 to rs485 converter), I've tried setting DtrEnable to true, or false, or switching it.

I've also tried switching RtsEnable when sending and receiving (following http://en.wikipedia.org/wiki/RS-232#RTS.2FCTS_handshaking).


I don't see anything else to try right now without resorting to different wiring. What could be wrong?


As requested, some code (it's only a snapshot after many attempts):

Open:

_serialPort = new SerialPort(comboBoxSerialPort.Text, 9600, Parity.None, 8, StopBits.One)
{
    WriteTimeout = 500,
    ReadTimeout = 500,
    Handshake = Handshake.None
};

_serialPort.Open();
_serialPort.DtrEnable = true;
_serialPort.RtsEnable = true;

Send:

_serialPort.RtsEnable = false;
_serialPort.Write(data, 0, data.Length);
_serialPort.RtsEnable = true;

Thread.Sleep(1);
_dataSent.Set();

Reader thread:

var port = form1._serialPort;
byte[] buffer = new byte[128];
int read = 0;
do
{
    Array.Clear(buffer, 0, buffer.Length);
    read = 0;

    try
    {
        form1._dataSent.WaitOne();

        //if (port.BytesToRead > 0)
        read = port.Read(buffer, 0, buffer.Length);
    }
    catch (TimeoutException)
    {
    }
    catch (Exception ex)
    {
        form1.Invoke(form1.AddErrorMethod, ex.ToString());
        continue;
    }

    if (read > 0)
    {
        form1.Invoke(form1.AddOutputMethod, ByteListToString(buffer));
    }

    Thread.Sleep(20);
}
while (_continue);

Note: data packets are 10 bytes long, in both directions.

jv42
  • 8,521
  • 5
  • 40
  • 64
  • Oh and I'm open to using a P/Invoke implementation of SerialPort, like the one proposed by Scott Hanselman here: http://www.hanselman.com/blog/PerformanceOfSystemIOPortsVersusUnmanagedSerialPortCode.aspx if it's a bug/limitation of the .NET Framework. – jv42 Sep 04 '13 at 12:58
  • One silly question: Have you confirmed that the device (the peripheral) actually sends out something on the wire? If yes, showing your code will help us to help you :) – user2019047 Sep 04 '13 at 15:23
  • @user2019047 that's not silly at all :) I can't confirm something is coming on the wire, but the manufacturer has confirmed that in their setup (not a Windows PC), the peripheral works as designed. – jv42 Sep 04 '13 at 15:44
  • I see you send a signal almost immediately after sending, and the read value will possibly be 0 the first time the signal is trapped. If I understand the code correctly, the next time a read is done will be when you send something and send a signal again. have you tried a breakpoint at the read = port. ... statement to see the value of read? maybe the datareceived handler is a better choice? – user2019047 Sep 04 '13 at 16:15
  • @user2019047 Yes I used signaling to communicate with a background thread doing polling on `SerialPort`. Read is the number of bytes actually read by the method (see http://msdn.microsoft.com/en-us/library/ms143549.aspx), it's always 0. It was originally used to offset into the buffer. – jv42 Sep 04 '13 at 16:39
  • @user2019047 As mentioned in my original question, I've tried the event handler approach, the event never got fired. – jv42 Sep 04 '13 at 16:40
  • Then either nothing gets transmitted from your device (unlikely?) or something is not configured correctly in the serial driver. What handshaking, if any, does the device expect? I see you are setting the RtsEnable property, from MSDN: "The Request to Transmit (RTS) signal is typically used in Request to Send/Clear to Send (RTS/CTS) hardware handshaking." From the few bytes being sent/received, maybe handshaking is not necessary? By the way, the code _serialPort = new SerialPort etc cannot compile. – user2019047 Sep 04 '13 at 17:41
  • @user2019047 I've managed to get a reply using one very specific hardware configuration (there was a wiring issue in that configuration) and using DtrEnable instead of RtsEnable to switch the line. I'll make a complete answer once I've checked all the variants. Thanks for your help! – jv42 Sep 05 '13 at 08:50

1 Answers1

3

I've fixed the issue on one hardware configuration (the RS485 card) by:

  • fixing the wiring: although it's called '2 wires', 4 pins/wires from the card need to be used, joined, 2 by transport wire.
  • using DtrEnable to indicate sending/receiving
  • waiting before enabling receive mode after a send.

My send code now looks like this:

// Enable send mode
SerialPort.DtrEnable = false;
SerialPort.Write(data, 0, data.Length);

// Wait a little, then enable response mode.
Thread.Sleep(15);
SerialPort.DtrEnable = true;
jv42
  • 8,521
  • 5
  • 40
  • 64
  • 1
    The waiting is required to deal with the RS-485 channel turning around. While the transmitter is enabled, (DTR false), the slave cannot send, but the setting is required to get the message sent. The delay needs to be tuned to the duration of the sent message (at 9600 bps, 10 bytes takes just over 10 ms). Serial timing is difficult on multiplexed channels with a high-level API. – Pekka Apr 12 '14 at 22:13
  • 1
    @Pekka Thanks for the comment. I hadn't thought in terms of bitrate to compute the required sleep time, it should help me improve reliability. – jv42 Apr 14 '14 at 13:02
  • 1
    while (SerialPort.BytesToWrite > 0) Thread.Yield(); is worth a try. – Hans Passant Oct 06 '22 at 12:59