[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1484
  • Last Modified:

serialport datareceived activating a timer

Hello,

I have a problem in the attached code.

I need my software to send some commands on serialport2 for x times every y seconds if a message is received on serialport1.

With this code the actions in the timer are never executed if the timere is activated by the datareceived fuction, but every thing works fine if it is activated by the button1. Moreover once the datarecived fuction is trigged the button stops to work.

Any ideas?



int n_timrs = 10, n_sent=0;
        public Form1()
        {
            InitializeComponent();
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            serialPort1.Open();
            serialPort2.Open();
            timer1.Enabled = false;
            timer1.Interval = 1000;
            serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(datareceived);
        }
        void datareceived(object s, SerialDataReceivedEventArgs e)
        {
            timer1.Enabled = true;
            serialPort2.Write("test" + Environment.NewLine);
            
        }
 
        private void timer1_Tick(object sender, EventArgs e)
        {
            serialPort2.Write("test" + Environment.NewLine);
            n_sent++;
            if (n_sent > n_timrs)
            {
                n_sent = 0;
                timer1.Enabled = false;
                MessageBox.Show(n_sent.ToString());
            }
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            timer1.Enabled = true;
            serialPort2.Write("test" + Environment.NewLine);
        }

Open in new window

0
borntofly
Asked:
borntofly
  • 7
  • 7
1 Solution
 
alex_pavenCommented:
For the dataReceived event to be triggered, you need to set the ReceivedBytesThreshold property of the serial port class to a non-zero value. Note that the DataReceived event will be triggered when a number of bytes are received, so if you receive twice as many bytes, the event will be triggered again. So for instance if you set ReceivedBytesThreshold = 2 and you receive 4 bytes (2 bytes and \r\n for instance), the event may be triggered twice. There is no guarrantee however that the number of bytes will correspond exactly to the number of times the event will be triggered.
0
 
alex_pavenCommented:
To clarify: in my example, with ReceivedBytesThreshold = 2 and 4 bytes received, the DataReceived event will be triggered at least once, but there is no guarrantee that it will be triggered a second time or not.
0
 
borntoflyAuthor Commented:
Hello,

thanks for your reply.

The DataReceived event is trigged (serialPort2.Write("test" + Environment.NewLine); is correctly executed). The problem is in the timer actions that are never executed.

I tried to put a MessageBox.Show at the end of the DataReceived event. If I do not close the MessageBox every thing works fine, but as soon as I close it the timer stops to execute.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
alex_pavenCommented:
Oh. Then I think the problem is that the DataReceived event is executed on a different thread. The best way would be to create a delegate and invoke it so that the timer runs on the form's thread. If you need specific code to help you out, ask further.
0
 
alex_pavenCommented:
Of course, I'm assuming the timer's Tick event is correctly bound to timer1_Tick.
0
 
borntoflyAuthor Commented:
Yes, please.

My brain is gone. I've been trying to solve it for 2 days now...:)

Thank you very much
0
 
borntoflyAuthor Commented:
Actually I solved it with a workaround, but it isn't really a proper solution.
I could not use the timer and trigger this fuction

private void test()
        {
           bool stop = false;
            do
            {
                if (DateTime.Now >= start.AddSeconds(freq))
                {
                    start = DateTime.Now;
                    serialPort2.Write("test" + Environment.NewLine);
                    n_sent++;
                    if (n_sent > n_timrs)
                    {
                        n_sent = 0;
                        stop = true;
                    }
 
                }
            } while (!stop);
        }

Open in new window

0
 
borntoflyAuthor Commented:
But I would prefer to use a timer.....
0
 
borntoflyAuthor Commented:
Can you explain me how to create and ivoke a delegate for the timer???

Thank you
0
 
alex_pavenCommented:
I can't really test the code now, and I can't guarrantee that it will solve your problem, but it should go something like this (I used a MethodInvoker rather than a more complicated delegate for simplicity):
 

void datareceived(object s, SerialDataReceivedEventArgs e)
{
  this.Invoke(new MethodInvoker(DoAction));
}
 
void DoAction() 
{
  timer1.Enabled = true;
  serialPort2.Write("test" + Environment.NewLine);
}

Open in new window

0
 
borntoflyAuthor Commented:
Thank you so much.

You saved me.
0
 
alex_pavenCommented:
If you want a detailed explanation of what happened, feel free to ask, it would be my pleasure :)
0
 
borntoflyAuthor Commented:
Actually I understud that the problem was that the datareceived event was executed in a different thread. What i do not undestend is why.

Thank you again
0
 
alex_pavenCommented:
The serial port is designed like that, so that when data is received you immediately get notified (I think because often synchronization is crucial when working with the serial port). With this code, the Invoke may introduce a delay of up to 10-15 milliseconds, I think.
The Windows.Forms.Timer however is very restrictive and only works on the thread that called it. You could have used a different kind of timer (System.Timers.Timer, or System.Threading.Timer) but those also fire their Tick (Elapsed) events on a different thread for greater precision, so depending on exactly what you needed to do when the timer fires, you may have needed Invoke in that case too.
0

Featured Post

How to Use the Help Bell

Need to boost the visibility of your question for solutions? Use the Experts Exchange Help Bell to confirm priority levels and contact subject-matter experts for question attention.  Check out this how-to article for more information.

  • 7
  • 7
Tackle projects and never again get stuck behind a technical roadblock.
Join Now