0

I've an UART device which I'm writing to it a command (via System.IO.Ports.SerialPort) and then immediately the device will respond.

So basically my approach is:

->Write to SerialPort->await Task.Delay->Read from the Port.

//The port is open all the time.
public async byte[] WriteAndRead(byte[] message){ 
port.Write(command, 0, command.Length);
await Task.Delay(timeout);
var msglen = port.BytesToRead;
    if (msglen > 0)
                {

                    byte[] message = new byte[msglen];
                    int readbytes = 0;

                    while (port.Read(message, readbytes, msglen - readbytes) <= 0)
                        ;

                    return message;

                    }

This works fine on my computer. But if I try it on another computer for example, the bytesToRead property is sometimes mismatched. There are empty bytes in it or the answer is not completed. (E.g. I get two bytes, if I expect one byte: 0xBB, 0x00 or 0x00, 0xBB)

I've also looked into the SerialPort.DataReceived Event, but it fires too often and is (as far as I understand) not really useful for this write and read approach. (As I expect the answer immediately from the device).

Is there a better approach to a write-and-read?

  • `int readbytes = 0; while (port.Read(message, readbytes, msglen - readbytes) <= 0);` So where do you update `readbytes` here? – itsme86 Mar 22 '18 at 20:28
  • Never lose the return value of Read(). And be sure to delete the Task.Delay() call, that just hides bugs in your code. It is quite incapable of fixing them. – Hans Passant Mar 22 '18 at 22:43
  • I outline an approach to handle incoming data here https://stackoverflow.com/questions/15124132/serial-port-polling-and-data-handling/15124287#15124287 You'd just have to build another abstraction to turn it into a sequence or use something like reactive extensions – Keith Nicholas Mar 23 '18 at 02:50
  • @HansPassant: The thing is, the device has a delay (somewere between 150-700ms). Usally this means, that -without waiting- SerialPort.BytesToRead is 0. (Indicating no data is there). Somehow I have to wait for the data. – user3410599 Mar 23 '18 at 19:52
  • Read() already waits for data, you have a guarantee that it will return at least 1 byte. So delaying by yourself doesn't accomplish anything useful. You do not need BytesToRead at all, simply pass msglen - totalbytes. Increment totalbytes by the Read() return value. – Hans Passant Mar 23 '18 at 21:28

1 Answers1

0

Read carefully the Remarks in https://msdn.microsoft.com/en-us/library/ms143549(v=vs.110).aspx You should not rely on the BytesToRead value to indicate message length. You should know, how much data you expect to read to decompose the message. Also, as @itsme85 noticed, you are not updating the readbytes, and therefore you are always writing received bytes to beginning of your array. Proper code with updating the readbytes should look like this:

int r;
while ((r = port.Read(message, readbytes, msglen - readbytes)) <= 0){
  readbytes += r;
}

However, during the time you will read data, more data can come and your "message" might be incomplete. Rethink, what you want to achieve.

Miq
  • 3,931
  • 2
  • 18
  • 32
  • I've implemented the fix & your suggestion. Now msglen is the expected bytes length. The thing is now sometimes the operation will timeout, because sometimes the device will recieve 9 bytes instead of 10. – user3410599 Mar 23 '18 at 19:55
  • then wrap the while with another while + some delay where you will wait until the message array is full. – Miq Mar 29 '18 at 07:31