favner85
favner85

Reputation: 63

Serial Port Async Read (non blocking) + Threads

Well, I've been strugling for the last 4 days with this SerialPort control in C# with no satisfactory results. Let me explain:

I have a device (Arduino UNO Board) that comunicates with a c# prog which simulates a scale (simple request/response pattern), the device sends a command sequence consisting of 3 bytes (asking for a weigh): CHR(27)+P+CHR(13) so the simulator responds with a simulated weight (I have sorted out how the device catches and parses this weight so this is not longer of the problem). Using the DataReceive event seems I'm loosing data using Serialport.Read() so I wasted this approach so far. The simulator HAVE TO BE ALWAYS listening for the said seq. of bytes and HAVE TO HAVE a GUI. I understand that for this I must use a Thread in order to prevent the GUI is locked (perhaps a backgroundworker?) and a sort of buffer which is shared between (now) this 2 threads and prevent the threads read/write at the same time to the buffer (do I need a state machine?) (I ask for help on this since I don't know if this is a good approach or my assumptions are wrong or if theres is a more easy way to solve this) so I'm asking for advice and (with lot of luck) code fragments or if you've faced to develop a similar app how you solved it.

I can provide the code I've done so far if necesary to clarify further more. Hope you can shed a light on this.

Thanks in advance.

UPDATE 1


This is the code i have so far:

ConcurrentQueue<byte> queue = new ConcurrentQueue<byte>();
....
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
   bool listening = true;
   while(listening)
   {
     if(serialPort.BytesToRead > 0)
     {
        byte b = (byte)serialPort.ReadByte();
        queue.Enqueue(b);
     }
   }
}

So since a command have to end with character 13 (CR in ASCII):

public string GetCommand()
{
    string ret = "";
    byte[] ba = new byte[1];
    byte b = (byte)' ';

    while(b!=13)
    {
       if(queue.TryDequeue(out b))
       {
          ba[0] = b;
          ret += ASCIIEncoding.ASCII.GetString([ba]);
       } 
    }
    return ret;
}

In order to test this GetCommand() method I call it from the main ui thread within a buton_click event but it hangs the app, do i need to create another thread to call GetCommand() ?

Upvotes: 1

Views: 6442

Answers (2)

Aditya
Aditya

Reputation: 31

This is ok for small amount of data. But if the data is bigger like if you are passing some http information, then the queue size may not be sufficient. So I think you should use a non-blocking type of architecture.

Upvotes: 3

Ian Mercer
Ian Mercer

Reputation: 39297

See this answer for how to implement the sending side.

For the reading side use a dedicated thread, in that thread read a message from the port, queue it up in a suitable concurrent data structure (e.g. a ConcurrentQueue) and immediately loop back to wait for the next input from the serial port.

Consume the input from the queue on a separate thread.

There may be more efficient ways but this one is easy to implement and foolproof.

Upvotes: 1

Related Questions