Daniil K
Daniil K

Reputation: 33

What is the right way to have multiple threads polling the same serial port?

I am very new to interacting with hardware. I am currently building a GUI in C# using the Windows Forms. I am using a serial port/usb to interact with a hardware device. What I am trying to achieve is to have multiple threads poll the device at different times. Some data such as temperature, current, power, etc. should be retrieved regularly (at least every second) and be updated on the GUI for the user to see. While other data will only be retrieved when the user presses a button on the form. Would multi-threading be the right approach to tackle this problem? If not, what would be a better solution? Examples would be greatly appreciated. Thank you for your time guys!

Update: I am trying to use the SerialPort.DataReceived Event as many of you have suggested and a terminator character '\r' to parse out individual replies from the serial port. Then I am invoking a method called DisplayText to handle that string. My problem now is I have no idea how to figure out what the string represents. Does it represent a temperature, a current, etc.

private char terminator = '\r';
private void SerialPorts_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        try
        {
            output += serialPort1.ReadExisting();
            if (output.IndexOf((char)this.terminator) > -1)
            {
                string workingString = output.Substring(0, output.IndexOf(terminator));

                output = output.Substring(output.IndexOf(terminator) + 1);

                this.Invoke(new EventHandler((s, a)=>DisplayText(s, a, workingString)));
            }

        }

        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    private void DisplayText(object sender, EventArgs e, string s)
    {
        Console.WriteLine(s); // for testing only
        richTextBox1.AppendText(s); // for testing only
    }

Upvotes: 2

Views: 3082

Answers (3)

Ihdina
Ihdina

Reputation: 1760

My problem now is I have no idea how to figure out what the string represents. Does it represent a temperature, a current, etc.

Use simple protocol to send temperature, a current, ect.

SOH + DATA + ETX + BCC

SOH = 0x02;

ETX = 0x03;

BCC = 0x00-0xFF, XOR value of of all DATA + ETX.

DATA = use ';' as delimiter between Temperature(Temp) and Current(Curr)

Example:

TempCmd = 0x01

TempValue = 32 celcius = 0x20

CurrCmd = 0x02

CurrValue = 1,4 ampere = 0x0E -> result must be divide with 10 on receiver.

SOH + DATA + ETX + BCC

SOH + TempCmd:TempValue; CurrCmd:CurrValue + ETX + BCC

02 + 01:20;02:0E + 03 + BCC

BCC = 01 xor ':' xor 20 xor ';' xor 02 xor ':' xor 0E xor 03

  • Send data from microcontroller "0201:20;02:0E03;XX" (send as string)

  • Receive data in computer:

    1. Detect SOH and ETX in package
    2. Validate BCC is true (completed package) or false (broken package)
    3. Parsing DATA with ; as delimiter // data1= 01:20 data2= 02:0E03
    4. Parsing data1 and data2 with : as delimiter // data1= 20 data2= 0E03
    5. Convert hex to decimal and divide data2 with 10 for get current with float format.

Upvotes: 0

schaazzz
schaazzz

Reputation: 568

You can guard all serial port access functionality using a mutex and design your application in such a way that the serial port is closed after each discrete operation (e.g. reading the temperature). One possible way to do this would be to write a wrapper API of-sorts which would:

  • Check if the mutex is unlocked (and return with an error code if it is locked),

  • Open the serial port

  • Read the required value

  • Close the serial port and clear the mutex

  • Return the read value

However, you are opening yourself up to all sorts of deadlocks with this solution and you will have to implement additional application level checks to prevent this.

As suggested above, a better solution would be to have a single thread periodically poll the serial port and continuously update all values in a data structure of your choice.

For the values which only need to be read after user input, you will have to implement a MessageQueue to request this data.


Update in response to the OPs edit

I would recommend either of the following 2 ways to approach the communication in your device:

  1. Implement a request/response mechanism where you send request for a specific data value (e.g. current) via serial port to your device, the device parses this request and returns the corresponding value
  2. Have your device return all values in a "frame" e.g. [SOF][D0...Dn][EOF], where "D0...Dn" is all your data in a pre-determined sequence; SOF and EOF can be any values of your choosing (you can also choose to skip these entirely and rely on the number of bytes to delimit frames).

In case you don't have the option of changing the code in the device, there must be some sort of order in which the values are reported by the device so maintaining a sequence number of the received data would be the way to go. But without knowing more details about what your device is doing, it would be difficult to suggest a solution.

Upvotes: 0

pigster
pigster

Reputation: 11

I think it is not a good idea to have multiple threads to poll single port and try to synchronize.

Better have one thread which does the polling and which stores all the values in some "middle" object (singleton, static fields, what you like) and than synchronize access to this storage.

So one threads polls the port, collects the values and stores them somewhere, than the other threads gets the values from there.

Upvotes: 1

Related Questions