13 Replies - 1961 Views - Last Post: 14 March 2016 - 06:24 AM Rate Topic: -----

#1 Domin8   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 04-October 15

How to make a functional never ending task/loop? - C# with serialport

Posted 12 March 2016 - 06:58 PM

So I'm creating a Windows Forms app that's gonna continously display updated sensor data from my Arduino microcontroller through serialport. I am also rather new to C# and even more so, Arduino. My issue is that when I try to display the updated sensor data through timers - the app crashes.

The arduino sends a string formatted like this 1;25 - where the first number 1 or 0, says whether or not the motion sensor has detected movement. The other number is the temperature.

class ArduinoComClass : SerialPort
{
    public string receivedData;
    private bool dataReady = false;
    public ArduinoComClass(string comport, int baudrate)
    {
        PortName = comport;
        BaudRate = baudrate;
        DataReceived += new SerialDataReceivedEventHandler(dataReceived);
        ReadTimeout = 2000;
        Open();
    }

    ~ArduinoComClass()
    {
        Close();
    }

    public void ConvertString(out string pirValue, out string tempValue)
    {
        string[] data = receivedData.Split(';');
        string string1 = data[0];
        if (string1 == "1")
        {
            pirValue = "Movement detected!";
        }
        else
        {
            pirValue = "No movement detected";
        }
        string string2 = data[1];
        tempValue = string2;
    }

    public bool DataReady
    {
        get
        {
            return dataReady;
        }
        set
        {
            dataReady = value;
        }
    }
    public void dataReceived(object sender, SerialDataReceivedEventArgs earg)
    {
        try
        {
            receivedData = ReadLine();
            dataReady = true;
        }
        // catch(TimeoutException e)
        catch (Exception e)
        {
            MessageBox.Show(e.Message);
            dataReady = false;
        }
    }
}

Now this is the code for the form I want to display the data in. It is very messed up due to me trying and trying different things and just kept on making it worse. I've tried tasks/threading but I get an error due to the fact that receivedData is in another thread.

static private ArduinoComClass ArduinoCom = new ArduinoComClass("COM3", 9600);
    private string pirValue;
    private string tempValue;

    public Main()
    {
        InitializeComponent();
        pirValue = "";
        tempValue = "";
        tmrAlarm.Start();
    }

    private void ShowSensorInfo()
    {
        while (true)
        {
            if (ArduinoCom.DataReady)
            {
                ArduinoCom.ConvertString(out pirValue, out tempValue);
                txtShowTemp.Text = tempValue;
                if (pirValue == "Movement detected!")
                {
                    picPIR.BackColor = Color.Red;
                }
                else
                {
                    picPIR.BackColor = Color.Lime;
                }

                //if (SystemInformation.PowerStatus.PowerLineStatus) <- Ignore for now
                ArduinoCom.DataReady = false;
            }
        }
    }

    private async void tmrAlarm_Tick(object sender, EventArgs e)
    {
        try
        {
            Task myTask = Task.Run(() => ShowSensorInfo());
            await myTask;
            myTask.Wait(2000);
        }
        catch (Exception error)
        {
            Console.WriteLine(error.Message);
        }
    }

So in short - how can I in an easy and effective way make my program show this live data without having the app crash?

Is This A Good Question/Topic? 0
  • +

Replies To: How to make a functional never ending task/loop? - C# with serialport

#2 snoopy11   User is offline

  • Engineering ● Software
  • member icon

Reputation: 1556
  • View blog
  • Posts: 4,930
  • Joined: 20-March 10

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 13 March 2016 - 12:50 AM

Where do you set...

Parity..
Databits..
Stopbits..
&
Handshake .. ??
Was This Post Helpful? 0
  • +
  • -

#3 Domin8   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 04-October 15

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 13 March 2016 - 03:48 AM

View Postsnoopy11, on 13 March 2016 - 12:50 AM, said:

Where do you set...

Parity..
Databits..
Stopbits..
&
Handshake .. ??


I am not familiar with those properties.. What does it do? Is it important?
Because transferring the data through serialport isn't the problem, the problem is actively displaying the data in the background while still using the UI.
Was This Post Helpful? 0
  • +
  • -

#4 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7485
  • View blog
  • Posts: 15,514
  • Joined: 16-October 07

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 13 March 2016 - 04:08 AM

I'd get rid of the timer. You have a perfectly good event. Don't fight it, hook into it.

Just off the top of my head, and with no way to test, this might work.
private SerialPort serialPort;
// private string pirValue;
// private string tempValue;

public Main() {
    InitializeComponent();
    this.serialPort = new SerialPort("COM3", 9600);
    this.serialPort.ReadTimeout = 2000;
    this.serialPort.DataReceived += new SerialDataReceivedEventHandler(ProcessDataReceived);
    this.serialPort.Open();
}

private void ProcessDataReceived(object sender, SerialDataReceivedEventArgs earg) {
    var data = this.serialPort.ReadLine().Split(';');
    ShowSensorInfo((data[0] == "1"), data[1]);
}

private void ShowSensorInfo(bool movement, string tempValue) {
    txtShowTemp.Text = tempValue;
    picPIR.BackColor = movement ? Color.Red : Color.Lime;
}



Now, if you wanted that to change at timer intervals, you'd simply save the result of ProcessDataReceived and do ShowSensorInfo on the tick. Note, a task in a timer seems a logical disconnect. You're in a form; think events.

Hope this helps.
Was This Post Helpful? 1
  • +
  • -

#5 Domin8   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 04-October 15

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 13 March 2016 - 05:34 AM

Hi Baavgai. Thank you for your reply.

I tried it out and I got a InvalidOperationException saying the data it's supposed to write to the textbox is from another thread.
Does this make sense, or would this perhaps be my wrong doing?

View Postbaavgai, on 13 March 2016 - 04:08 AM, said:

I'd get rid of the timer. You have a perfectly good event. Don't fight it, hook into it.


View Postbaavgai, on 13 March 2016 - 04:08 AM, said:

Note, a task in a timer seems a logical disconnect. You're in a form; think events.Hope this helps.


Also, could you elaborate on what you meant by this? Getting rid of the timer and thinking events.
The only reason I tried using a timer is because it's the only way I know on how to create repetitive job, that I maybe hoped could just sit there and do it's thing while you can watch and/or interract with the UI meanwhile. I've searched around and I've seen people saying you have to use Dispatch.Invoke or Form.Invoke. However I am very little familiar with what that does.
Was This Post Helpful? 0
  • +
  • -

#6 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7056
  • View blog
  • Posts: 23,994
  • Joined: 05-May 12

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 13 March 2016 - 09:48 AM

Yes, you need to use Invoke() so that you can effectively set a UI control which lives in the UI thread from whatever thread that the serial events are firing from. All you need to do is read the documentation to see how to use it.

The alternative to not using Invoke(), is to make sure that you are using WinForms timers. When those timer events fire, they will be in the UI thread and do you can set display controls without having to jump through hoops.
Was This Post Helpful? 0
  • +
  • -

#7 snoopy11   User is offline

  • Engineering ● Software
  • member icon

Reputation: 1556
  • View blog
  • Posts: 4,930
  • Joined: 20-March 10

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 13 March 2016 - 11:38 AM

Yes,


for an arduino the default is 8 data bits, no parity, one stop bit.
don't know what the default is for the SerialPort class.

Would a BackgroundWorker class not be more appropriate here ?

https://msdn.microso...(v=vs.110).aspx

That way you could get rid of the timer and let the BackgroundWorker class take care of everything and not hang the UI.

Regards

Snoopy.
Was This Post Helpful? 0
  • +
  • -

#8 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7056
  • View blog
  • Posts: 23,994
  • Joined: 05-May 12

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 13 March 2016 - 11:51 AM

Unfortunately, the background worker is the worse of both worlds.

The background worker and the serial class both use thread pools, and so he still has the same issue of trying to update the UI from the non-UI thread.

The background worker and the timer will both have the same issue where code has to be correctly written to recognize when data is available.
Was This Post Helpful? 0
  • +
  • -

#9 snoopy11   User is offline

  • Engineering ● Software
  • member icon

Reputation: 1556
  • View blog
  • Posts: 4,930
  • Joined: 20-March 10

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 13 March 2016 - 12:14 PM

I see...

Oh well it was just an idea...

back to the drawing board :)
Was This Post Helpful? 0
  • +
  • -

#10 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 14 March 2016 - 01:19 AM

Am I missing something? It should be straightforward easy to make UI code like this thread-safe, or so I thought. A check if an invoke is needed on the control deisred to be updated has worked for me just fine so far. As an example this is how I write to a rich text box. It seems to have never caused me threading issues as a result but that is a limited experience int he world of programming so take it with a grain of salt.

      private static void AppendText(this RichTextBox box, string text, Color color, params object[] args)
        {
            text = string.Format(text, args);
            if (color == Color.Empty)
            {
                box.AppendText(text);
                return;
            }

            box.Selectionstart = box.TextLength;
            box.SelectionLength = 0;

            box.SelectionColor = color;
            box.AppendText(text);
            box.SelectionColor = box.ForeColor;

            box.Selectionstart = box.TextLength;
            box.ScrollToCaret();
        }


        private static void AppendLine(this RichTextBox box, string text, Color color, params object[] args)
        {
            box.AppendText($@"{text}", color == Color.Empty ? box.ForeColor : color, args);
        }


        private static void InvokeAppendLine(this RichTextBox box, string text, Color color, params object[] args)
        {
            box.Invoke((MethodInvoker) delegate { box.AppendLine(text, color, args); });
        }



Was This Post Helpful? 0
  • +
  • -

#11 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7056
  • View blog
  • Posts: 23,994
  • Joined: 05-May 12

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 14 March 2016 - 04:46 AM

Yes, it is straight forward if one takes time to understand the conditions when Invoke is needed, and when it is not. I feel that the issue is that our OP started out by jumping into WinForms right away without understanding this concept.
Was This Post Helpful? 0
  • +
  • -

#12 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7485
  • View blog
  • Posts: 15,514
  • Joined: 16-October 07

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 14 March 2016 - 05:01 AM

Yeah, the thread thingy is odd. I thought I might write a test for this.

First, because I want something with an event to spew data at me, a silly class:
class RandomEventThingy {
    public delegate void DataReceivedEventHandler(object sender, int value);
    public event DataReceivedEventHandler DataReceived;
    private void OnDataReceived(int n) {
        if (DataReceived != null) {
            DataReceived(this, n);
        }
    }
    private readonly Random rnd = new Random();
    private Task task = null;

    public void Start() {
        if (task == null) {
            task = Task.Run( () => {
                while(true) {
                    var n = rnd.Next(1000) + 500;
                    OnDataReceived(n);
                    Thread.Sleep(n);
                }
            });
        }
    }

    public void Stop() {
        if (task!=null) {
            task.Dispose();
            task = null;
        }
    }
}



Now, to a simple form to chew on that:
public partial class Form1 : Form {
    private readonly RandomEventThingy ret;
    private readonly Color[] items;
    public Form1() {
        InitializeComponent();
        items = new Color[] { Color.Red, Color.Green, Color.Blue };
        ret = new RandomEventThingy();
        ret.DataReceived += (s,v) => ShowInfo(v);

    }
    private void Form1_Load(object sender, EventArgs e) {
        ret.Start();
    }
    private void ShowInfo(int value) {
        var pick = this.items[value % this.items.Length];
        txtShowTemp.Text = pick.ToString();
        picPIR.BackColor = pick;
    }

}



And, the results:
System.InvalidOperationException was unhandled by user code
  HResult=-2146233079
  Message=Cross-thread operation not valid: Control 'txtShowTemp' accessed from a thread other than the thread it was created on.
  Source=System.Windows.Forms



Cool, I reproduced your error! Now, to apply a fix:
public partial class Form1 : Form {
    private readonly RandomEventThingy ret;
    private readonly Color[] items;
    private int hitCount;
    public Form1() {
        InitializeComponent();
        items = new Color[] { Color.Red, Color.Green, Color.Blue };
        ret = new RandomEventThingy();
        ret.DataReceived += (s,v) => ProxyCall(v);
        hitCount = 0;
    }
    private void Form1_Load(object sender, EventArgs e) {
        ret.Start();
    }

    private void ProxyCall(int value) {
        this.Invoke((MethodInvoker)delegate { this.ShowInfo(value); });
    }

    public void ShowInfo(int value) {
        var pick = this.items[value % this.items.Length];
        txtShowTemp.Text = (++hitCount) + " " + pick.ToString();
        picPIR.BackColor = pick;

    }

}



And, with super wonky Invoke syntax as suggested by Skydiver we're off and running.

Hopefully this shows what I mean by thinking in events. An event fires when it fires. Rather create yet another thread to monitor events, try to consume them as directly as possible. Threads are messy, confusing, and extremely danger prone. An event is a way of handling aysnc chaos and the more you can stay out of the way of that, the less pain you'll suffer.

Hope this helps.
Was This Post Helpful? 2
  • +
  • -

#13 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7056
  • View blog
  • Posts: 23,994
  • Joined: 05-May 12

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 14 March 2016 - 06:12 AM

Unfortunately, for most WinForm users, their first exposure with events are button click events and (Windows message) timer events. They always fire on the same thread as the form. Even the first introduction to the BackgroundWorker class doesn't make it obvious that work is being done on another thread because the progrees events fired by that class are routed back to the original UI thread. It's when people start to update their UI from within the DoWork() that they start discovering that there are problems.

What is even more disconcerting is for the programmer transitioning from Win32 API programming to WinForms programming. Although the Win32 API documentation has some warnings about sending messages across threads, you can normally ignore those warnings and send messages across threads with impunity. (Even some of Microsoft's code does this.) For example, you can update the progress dialog's text and progress bar from another thread. The only time you would start running into problems with the cross thread messages are when you start playing with COM objects, and depending on how the COM objects are implemented, you may or may not run into a deadlock as COM under the covers sends messages across threads on your behalf.

As frustrating as it is, I think that the WinForms designers made the right decision to try to enforce the "no cross thread window messages" despite any protest that season Win32 API programmers may put up saying that "it works just fine in unmanaged code". It frees them from have to deal with any incoming bugs where a programmer may report that "I put FizzBin ActiveX control on my form, and then I fired off another thread to tell the control to start doing work and my application just hangs."
Was This Post Helpful? 0
  • +
  • -

#14 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Re: How to make a functional never ending task/loop? - C# with serialport

Posted 14 March 2016 - 06:24 AM

Quote

Yes, it is straight forward if one takes time to understand the conditions when Invoke is needed


With out testing yet and having not used WinForms much lately, I think this would work just fine for basic text box appends with out having to worry about that too much. It's great to understand why invoking the control is needed at times of course, though.

        public static void AppendTextSafe(this TextBox box, string text, bool withArgs = false,params object[] args)
        {
            var msg = !withArgs ? text : string.Format(text, args);

            if (!box.InvokeRequired)
            {
                box.AppendText(msg);
            }

            else
            {
                box.Invoke((MethodInvoker) delegate { box.AppendText(msg); });
            }
        } 

This post has been edited by JacobH: 14 March 2016 - 06:25 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1