|
-
Nov 3rd, 2011, 03:03 PM
#1
Thread Starter
Addicted Member
Timer delay
Hi,
I have more than one packet to be sent out from serial port but need to put a delay between each packet. But I don't want to use sleep to delay because interrupts don't work any more while sleeping. Anything else I can use for delay?
Thanks,
Simon
-
Nov 3rd, 2011, 03:53 PM
#2
Addicted Member
Re: Timer delay
I've found this to work in vb 2010. This will cause a 4 second delay
Delay(4)
Sub Delay(ByVal dblSecs As Double)
Const OneSec As Double = 1.0# / (1440.0# * 60.0#)
Dim dblWaitTil As Date
Now.AddSeconds(OneSec)
dblWaitTil = Now.AddSeconds(OneSec).AddSeconds(dblSecs)
Do Until Now > dblWaitTil
Application.DoEvents() ' Allow windows messages to be processed
Loop
End Sub
-
Nov 3rd, 2011, 04:35 PM
#3
Re: Timer delay
That's the worst possible solution. It's what's known as a busy wait, and will take your CPU to 100% for the duration of the wait. If you run the app on a laptop, you might actually see the battery life decrease when using such a hideous construct.
The solution is to use a timer.
One option would be to enqueue all of your messages. You could even use a Queue(of T) to do this such that you would really be enqueuing them. Then, every time the timer ticked, you'd strip the next item off the queue and send it.
My usual boring signature: Nothing
 
-
Nov 3rd, 2011, 06:20 PM
#4
Re: Timer delay
If you use a background thread to send the data then threading.thread.sleep will work or use the approach that shaggy suggested.
-
Nov 3rd, 2011, 06:22 PM
#5
Re: Timer delay
That may be the best alternative of them all.
My usual boring signature: Nothing
 
-
Nov 4th, 2011, 07:47 AM
#6
Thread Starter
Addicted Member
Re: Timer delay
 Originally Posted by Mike54
I've found this to work in vb 2010. This will cause a 4 second delay
Delay(4)
Sub Delay(ByVal dblSecs As Double)
Const OneSec As Double = 1.0# / (1440.0# * 60.0#)
Dim dblWaitTil As Date
Now.AddSeconds(OneSec)
dblWaitTil = Now.AddSeconds(OneSec).AddSeconds(dblSecs)
Do Until Now > dblWaitTil
Application.DoEvents() ' Allow windows messages to be processed
Loop
End Sub
Thanks a lot! I added in my code (VS 2008), it passed compiling. I will test the functionality and could need your more help... Thanks again. Have a great weekend.
-
Nov 4th, 2011, 10:18 AM
#7
Re: Timer delay
 Originally Posted by zhengsimon
Thanks a lot! I added in my code (VS 2008), it passed compiling. I will test the functionality and could need your more help... Thanks again. Have a great weekend.
Many of the more experience posters will tell you that this is NOT how this should be done. The presence of Application.DoEvents() is a dead give away. The code may seems to produce the correct results, but can lead to errors that are very difficult to find later.
-
Nov 4th, 2011, 10:22 AM
#8
Thread Starter
Addicted Member
Re: Timer delay
 Originally Posted by Shaggy Hiker
That's the worst possible solution. It's what's known as a busy wait, and will take your CPU to 100% for the duration of the wait. If you run the app on a laptop, you might actually see the battery life decrease when using such a hideous construct.
The solution is to use a timer.
One option would be to enqueue all of your messages. You could even use a Queue(of T) to do this such that you would really be enqueuing them. Then, every time the timer ticked, you'd strip the next item off the queue and send it.
Can you describe a little bit more detailed or give me an example? Thanks.
-
Nov 4th, 2011, 10:25 AM
#9
Re: Timer delay
DON'T USE THAT CODE!!!!
Yes, it WILL pass compile, and it will even work as you expect, but that doesn't mean it's a good idea. Making use of a busy wait like that will have physical consequences, which isn't something that can be said about most programs.
It seems like there is somebody suggesting a busy wait whenever anybody asks for a way to pause without using thread.sleep (which has other problems). A busy wait is so easy to implement that people don't have any difficultly making it work. What they don't understand is that it is downright harmful. While that loop is running, your CPU will be absolutely pegged out. That's a nasty thing to do to people even if you have multiple cores, because that core is fully occupied doing absolutely nothing. You aren't really waiting, you are frantically busy running in place. The two are not the same.
I can't make it any more plain: That code will work, but DON'T USE IT!!!
My usual boring signature: Nothing
 
-
Nov 4th, 2011, 10:26 AM
#10
Re: Timer delay
 Originally Posted by zhengsimon
Hi,
I have more than one packet to be sent out from serial port but need to put a delay between each packet. But I don't want to use sleep to delay because interrupts don't work any more while sleeping. Anything else I can use for delay?
Thanks,
Simon
Why do you need to delay between the packets?
-
Nov 4th, 2011, 10:27 AM
#11
Thread Starter
Addicted Member
Re: Timer delay
 Originally Posted by Shaggy Hiker
DON'T USE THAT CODE!!!!
Yes, it WILL pass compile, and it will even work as you expect, but that doesn't mean it's a good idea. Making use of a busy wait like that will have physical consequences, which isn't something that can be said about most programs.
It seems like there is somebody suggesting a busy wait whenever anybody asks for a way to pause without using thread.sleep (which has other problems). A busy wait is so easy to implement that people don't have any difficultly making it work. What they don't understand is that it is downright harmful. While that loop is running, your CPU will be absolutely pegged out. That's a nasty thing to do to people even if you have multiple cores, because that core is fully occupied doing absolutely nothing. You aren't really waiting, you are frantically busy running in place. The two are not the same.
I can't make it any more plain: That code will work, but DON'T USE IT!!!
I am sorry not to make it clear. I meant can you describe your TIMER solution a little more detailed or give me an example? Thanks.
-
Nov 4th, 2011, 10:32 AM
#12
Re: Timer delay
Sorry, a series of things got posted while I was frothing over that busy wait.
The proper solution is not quite as easy to show, because it involves a conceptual shift. The proper way to do this is to add a timer component to your form. Set the interval of that timer to 4000 (because the interval is in milliseconds, so 4000 milliseconds would be 4 seconds). Double click on the timer to go to the timer tick event handler. In that handler is where the serialport Send operation should be located.
The other part is setting up the things to send. I don't know what those packets are, but I would guess that they are arrays of bytes. Therefore, add a Queue (of Byte()) at form scope. Wherever you are currently doing the sending, change the code so that you are enqueueing each packet rather than sending them. Once you have enqueued all your packets you can start the timer.
What is happening in the timer tick is that it checks the queue to see that the count > 0. If it is, dequeue the next item and send it. Once the count = 0, stop the timer.
That's the way to do it in a single thread. DB has a solution that would send all of those items in a background thread, which has certain advantages, but might be more difficult to implement if you aren't familiar with threads. Either way, the queue is a good idea. In fact, you could just leave the timer running and whenever you enqueue something, it will be sent when the timer gets to it.
My usual boring signature: Nothing
 
-
Nov 4th, 2011, 10:38 AM
#13
Re: Timer delay
 Originally Posted by Shaggy Hiker
Sorry, a series of things got posted while I was frothing over that busy wait.
The proper solution is not quite as easy to show, because it involves a conceptual shift. The proper way to do this is to add a timer component to your form. Set the interval of that timer to 4000 (because the interval is in milliseconds, so 4000 milliseconds would be 4 seconds). Double click on the timer to go to the timer tick event handler. In that handler is where the serialport Send operation should be located.
The other part is setting up the things to send. I don't know what those packets are, but I would guess that they are arrays of bytes. Therefore, add a Queue (of Byte()) at form scope. Wherever you are currently doing the sending, change the code so that you are enqueueing each packet rather than sending them. Once you have enqueued all your packets you can start the timer.
What is happening in the timer tick is that it checks the queue to see that the count > 0. If it is, dequeue the next item and send it. Once the count = 0, stop the timer.
That's the way to do it in a single thread. DB has a solution that would send all of those items in a background thread, which has certain advantages, but might be more difficult to implement if you aren't familiar with threads. Either way, the queue is a good idea. In fact, you could just leave the timer running and whenever you enqueue something, it will be sent when the timer gets to it.
I agree with almost all of this. The one difference is that the timer can run all the time, firing every 4 seconds. Inside the tick event check to see if the queue has an item, and if so, send it.
-
Nov 4th, 2011, 10:42 AM
#14
Re: Timer delay
Take a look at the very final sentence. That's a poorly constructed paragraph, since I started talking about threading and tacked a totally different concept onto the end, but I covered it. I'd leave the timer running all the time, too.
Now that I think of it, a 4 second delay is excessively long, though.
My usual boring signature: Nothing
 
-
Nov 4th, 2011, 10:46 AM
#15
Re: Timer delay
It is not clear that the OP ever requested a 4 second delay, or why they needed a delay at all. The delay came from the run in place code from what I can tell.
I should have read with more care shag, sorry.
-
Nov 4th, 2011, 10:49 AM
#16
Thread Starter
Addicted Member
Re: Timer delay
 Originally Posted by dbasnett
Why do you need to delay between the packets?
1, Need to wait all bytes received before sending next packet
2, Need to retry after a period of time if no ACK received
-
Nov 4th, 2011, 11:50 AM
#17
Re: Timer delay
 Originally Posted by zhengsimon
1, Need to wait all bytes received before sending next packet
2, Need to retry after a period of time if no ACK received
Is this from the perspective of the PC or the device at the other end of the RS232 port?
-
Nov 4th, 2011, 12:03 PM
#18
Thread Starter
Addicted Member
Re: Timer delay
 Originally Posted by dbasnett
Is this from the perspective of the PC or the device at the other end of the RS232 port?
This side is PC and the other side is a RS232/ISO 9141 converter.
-
Nov 4th, 2011, 01:05 PM
#19
Thread Starter
Addicted Member
Re: Timer delay
 Originally Posted by Shaggy Hiker
Sorry, a series of things got posted while I was frothing over that busy wait.
The proper solution is not quite as easy to show, because it involves a conceptual shift. The proper way to do this is to add a timer component to your form. Set the interval of that timer to 4000 (because the interval is in milliseconds, so 4000 milliseconds would be 4 seconds). Double click on the timer to go to the timer tick event handler. In that handler is where the serialport Send operation should be located.
The other part is setting up the things to send. I don't know what those packets are, but I would guess that they are arrays of bytes. Therefore, add a Queue (of Byte()) at form scope. Wherever you are currently doing the sending, change the code so that you are enqueueing each packet rather than sending them. Once you have enqueued all your packets you can start the timer.
What is happening in the timer tick is that it checks the queue to see that the count > 0. If it is, dequeue the next item and send it. Once the count = 0, stop the timer.
That's the way to do it in a single thread. DB has a solution that would send all of those items in a background thread, which has certain advantages, but might be more difficult to implement if you aren't familiar with threads. Either way, the queue is a good idea. In fact, you could just leave the timer running and whenever you enqueue something, it will be sent when the timer gets to it.
Appreciate it!
-
Nov 4th, 2011, 03:24 PM
#20
Thread Starter
Addicted Member
Re: Timer delay
 Originally Posted by Shaggy Hiker
Sorry, a series of things got posted while I was frothing over that busy wait.
The proper solution is not quite as easy to show, because it involves a conceptual shift. The proper way to do this is to add a timer component to your form. Set the interval of that timer to 4000 (because the interval is in milliseconds, so 4000 milliseconds would be 4 seconds). Double click on the timer to go to the timer tick event handler. In that handler is where the serialport Send operation should be located.
The other part is setting up the things to send. I don't know what those packets are, but I would guess that they are arrays of bytes. Therefore, add a Queue (of Byte()) at form scope. Wherever you are currently doing the sending, change the code so that you are enqueueing each packet rather than sending them. Once you have enqueued all your packets you can start the timer.
What is happening in the timer tick is that it checks the queue to see that the count > 0. If it is, dequeue the next item and send it. Once the count = 0, stop the timer.
That's the way to do it in a single thread. DB has a solution that would send all of those items in a background thread, which has certain advantages, but might be more difficult to implement if you aren't familiar with threads. Either way, the queue is a good idea. In fact, you could just leave the timer running and whenever you enqueue something, it will be sent when the timer gets to it.
In the following code, I enqueue SendQueue. After I added the first array, I updated SendBuffer() and tried to add one more object into SendQueue. However, the first one was changed to the same as the second. Do I have to declare another array to store the object first before I add it into SendQueue?
Public SendQueue As Queue(Of Byte()) = New Queue(Of Byte())()
Private Sub ReadSNButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ReadSNButton.Click
Dim SendBuffer() As Byte = New Byte() {0}
Array.Resize(SendBuffer, 20)
BytesSent = 5
SendBuffer(0) = &H43
SendBuffer(3) = READ_SERIAL_NUM_L
SendBuffer(4) = CalCheckSum(SendBuffer, BytesSent - 2) 'checksum
SequenceRunning = False 'regular message instead of sequence
SendQueue.Enqueue(SendBuffer)
BytesSent = 5
SendBuffer(0) = &H44 'format
SendBuffer(3) = READ_SERIAL_NUM_H
SendBuffer(4) = CalCheckSum(SendBuffer, BytesSent - 2) 'checksum
SendQueue.Enqueue(SendBuffer)
End Sub
-
Nov 4th, 2011, 05:54 PM
#21
Re: Timer delay
I think it needs to look something like this:
Code:
Public Class Form1
Dim WithEvents sp As New IO.Ports.SerialPort
Dim dataRcrvd As New Threading.EventWaitHandle(True, Threading.EventResetMode.AutoReset)
Private Sub Form1_Shown(sender As Object, _
e As System.EventArgs) Handles Me.Shown
'start a background thread to send messages
sendMessThrd = New Threading.Thread(AddressOf sendMessages)
sendMessThrd.IsBackground = True
sendMessThrd.Start()
End Sub
Dim sendMessThrd As Threading.Thread
Dim myQ As New sendQ
Private Sub sendMessages()
Do
dataRcrvd.WaitOne(100) 'wait for data to be received. the number IS the timeout
If myQ.hasMessage Then
If myQ.Ack Then myQ.RemoveTopEntry() 'if the message previously sent was acked then delete it
If myQ.hasMessage Then
Dim b() As Byte = myQ.Message
If sp.IsOpen Then sp.Write(b, 0, b.Length)
End If
End If
Loop
End Sub
Private Sub sp_DataReceived(sender As Object, _
e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles sp.DataReceived
'place code here to receive the input
'process it and set / clear the ack as required
'then
dataRcrvd.Set() 'notify
End Sub
End Class
Class sendQ
Private _queue As New Queue(Of queueEntry)
''' <summary>
''' adds a message to the queue
''' </summary>
''' <param name="message">the byte array to add</param>
''' <remarks></remarks>
Public Sub Add(message() As Byte)
Dim foo As New queueEntry(message)
Me._queue.Enqueue(foo)
End Sub
Public Function Message() As Byte()
Dim foo As queueEntry = Me._queue.Peek
Return foo.buffer
End Function
''' <summary>
''' check / set ack status
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property Ack() As Boolean
Get
Dim foo As queueEntry = Me._queue.Peek
Return foo.ack
End Get
Set(ByVal value As Boolean)
Me._queue(0).ack = value
End Set
End Property
Public Sub RemoveTopEntry()
Dim foo As queueEntry = Me._queue.Dequeue
End Sub
Public Function hasMessage() As Boolean
If Me._queue.Count > 0 Then Return True Else Return False
End Function
End Class
Class queueEntry
Public buffer() As Byte
Public ack As Boolean
Public Sub New(message() As Byte)
Me.buffer = New Byte() {}
Array.Resize(Me.buffer, message.Length)
Array.Copy(message, Me.buffer, message.Length)
Me.ack = False
End Sub
End Class
-
Nov 4th, 2011, 10:00 PM
#22
Re: Timer delay
I like the route DB is taking, but let me explain what happened when you added the two arrays.
The problem was that you didn't add two arrays. You added only one array, but you added it twice. You created the sendBuffer in this line:
Dim SendBuffer() As Byte = New Byte() {0}
You then sized the buffer in the next line, then filled it, and enqueued it. Remember that SendBuffer doesn't hold the actual buffer. It holds the address in memory where the buffer resides. What you actually added to the queue is the address of the object. That's fine, as the difference doesn't matter. However, you then changed around a few bytes in the buffer and added it again. You didn't change what SendBuffer was pointing to, so it is still holding the address of the same array of bytes. You changed the values in those bytes, but it's the same set of bytes. So when you added SendBuffer again, you added the same address of the same bytes. They had different values, but they didn't reside in a different place.
What you needed to do was to recreate SendBuffer before you added the second message. You could probably have done that by simply calling ReDim again, or you could create a new array with New.
My usual boring signature: Nothing
 
-
Nov 9th, 2011, 02:47 PM
#23
Thread Starter
Addicted Member
Re: Timer delay
 Originally Posted by Shaggy Hiker
Sorry, a series of things got posted while I was frothing over that busy wait.
The proper solution is not quite as easy to show, because it involves a conceptual shift. The proper way to do this is to add a timer component to your form. Set the interval of that timer to 4000 (because the interval is in milliseconds, so 4000 milliseconds would be 4 seconds). Double click on the timer to go to the timer tick event handler. In that handler is where the serialport Send operation should be located.
The other part is setting up the things to send. I don't know what those packets are, but I would guess that they are arrays of bytes. Therefore, add a Queue (of Byte()) at form scope. Wherever you are currently doing the sending, change the code so that you are enqueueing each packet rather than sending them. Once you have enqueued all your packets you can start the timer.
What is happening in the timer tick is that it checks the queue to see that the count > 0. If it is, dequeue the next item and send it. Once the count = 0, stop the timer.
That's the way to do it in a single thread. DB has a solution that would send all of those items in a background thread, which has certain advantages, but might be more difficult to implement if you aren't familiar with threads. Either way, the queue is a good idea. In fact, you could just leave the timer running and whenever you enqueue something, it will be sent when the timer gets to it.
Hi, your solution really helps. I tried to put all what I need to send to queue, they work well. But sometimes I still saw buffer messed up. I tried to change my code but there are some issues that made the compiling stop. In order to tell you clearly, I only attach a few lines of my code as follows. Here is the issue: if I put the SerialPort.Write inside the IF statement, compiling was fine. But if I move SerialPort.Write outside the IF statement body, there was an error message: Object variable or With block variable not set. What does that mean? I declared testArray as object.
If SendQueue.Count > 0 Then 'if SendQueue is not empty
testArray = SendQueue.Dequeue
End If
SerialPort.Write(testArray, 0, testArray.Length)
-
Nov 9th, 2011, 07:33 PM
#24
Re: Timer delay
When you get that particular message, take a close look at the line that is causing the error. The only way that can happen is if one of the objects is Nothing. Find the object that is Nothing, and all will be well. In this case, there are only two objects in the statement: SerialPort and testArray. Highlight each in turn and you will find that one of them is Nothing (you may have to highlight it and press Shift+F9, because the tooltip seems to often fail when an object is Nothing). I would guess that testArray is Nothing when the error occurs. After all, if SendQueue.Count = 0, then testArray won't be set to anything, which means it will be Nothing, which means that the error is coming from testArray.Length. The reason is that if testArray is Nothing, you can't call a property of it, because Nothing has no properties. So you only want that Write statement inside the If block, because there is nothing to write if there is nothing in the queue.
My usual boring signature: Nothing
 
-
Nov 10th, 2011, 09:26 AM
#25
Thread Starter
Addicted Member
Re: Timer delay
 Originally Posted by Shaggy Hiker
When you get that particular message, take a close look at the line that is causing the error. The only way that can happen is if one of the objects is Nothing. Find the object that is Nothing, and all will be well. In this case, there are only two objects in the statement: SerialPort and testArray. Highlight each in turn and you will find that one of them is Nothing (you may have to highlight it and press Shift+F9, because the tooltip seems to often fail when an object is Nothing). I would guess that testArray is Nothing when the error occurs. After all, if SendQueue.Count = 0, then testArray won't be set to anything, which means it will be Nothing, which means that the error is coming from testArray.Length. The reason is that if testArray is Nothing, you can't call a property of it, because Nothing has no properties. So you only want that Write statement inside the If block, because there is nothing to write if there is nothing in the queue.
Thank you for your quick reply. I might have misunderstood Dequeue. If there is one element in Queue, and while using Dequeue, is the element copied to testArray and the Queue becomes empty? Why is testArray NOTHING? I tried to change .Dequeue to .Peek and dequeue later, it worked well. I thought that the only difference between .Peek() and .Dequeue() is the oldest element is removed by .Dequeue. But both copy the oldest element to testArray. Correct me...
-
Nov 10th, 2011, 11:33 AM
#26
Re: Timer delay
Dequeue should return the next item from the queue, and remove it from the queue. Peek should return the next item, but not remove it from the queue.
My usual boring signature: Nothing
 
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|