-
problem in handling multiple Bytes received using Serial Port
Hello All,
I'am Developing an application in VB.Net in which I need to receive 8 Bytes from Microcontroller at once and then process those 8 bytes to plot a graph of those values using drawline function.
Then again next set of 8 Bytes are received & processed and plotting continues. For testing purpose I'am sending a sine wave from freq generator and need to plot the same on vb application.
Ia'm receiving 8 Bytes because my application needs to send four ADC Input Pins data which is of 12 Bits. So to send the complete data i need to break the data into two bytes per one channel input and then again OR(with Bit shifting) the received 2 Bytes to get it back to 12 Bits on the PC side.
My UART Speed is 115200 Bps.
First I send a single channel data ( 2 bytes from the microcontroller )and tried receiving it via below code
Code:
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim numberOfBytesToRead As Integer
numberOfBytesToRead = SerialPort1.BytesToRead
' Create a byte array large enough to hold the bytes to be read.
Dim newReceivedData(numberOfBytesToRead - 1) As Byte
' Read the bytes into the byte array.
SerialPort1.Read(newReceivedData, 0, numberOfBytesToRead)
Me.Invoke(New EventHandler(AddressOf Graph2Delegate))
My problem is I'am not able to handle the received buffer properly.
How to retrieve the data in delegate function and clear the buffer again to receive new Bytes?
kindly guide me.
-
Re: problem in handling multiple Bytes received using Serial Port
Is that call to Read populating the array correctly? If you want to actually pass the array to your graphing method then you need to declare that method with a parameter of that type and then Invoke it with delegate with a matching signature, e.g.
vb.net Code:
Private Sub DrawGraph(data As Byte())
vb.net Code:
Me.Invoke(New Action(Of Byte())(AddressOf DrawGraph), newReceivedData)
Notice that the method doesn't have "Delegate" in the name, which doesn't make sense for a delegate, never mind a method. Also, I do hope that you're not actually drawing on the form or another control in that method. Drawing on an Image and displaying that would be OK but if you're drawing on a control then you need to be doing that in its Paint event handler.
-
Re: problem in handling multiple Bytes received using Serial Port
You can't assume that one DataReceived event will contain all of the bytes sent. The following code accumulates the bytes received. See the note in the code about what your protocol message length is. I did not address the issues JMC brought up in post#2. A combo of his code and mine should help.
Code:
Private buffer As New List(Of Byte)
Private Sub SerialPort1_DataReceived(sender As Object,
e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim BytesRead As Integer = 0
Dim numberOfBytesToRead As Integer = SerialPort1.BytesToRead
' Create a byte array large enough to hold the bytes to be read.
Dim newReceivedData(numberOfBytesToRead - 1) As Byte
' Read the bytes into the byte array.
BytesRead = SerialPort1.Read(newReceivedData, 0, numberOfBytesToRead)
If BytesRead > 0 Then
If BytesRead <> numberOfBytesToRead Then
Array.Resize(newReceivedData, BytesRead)
End If
buffer.AddRange(newReceivedData) 'accumulate in buffer
End If
'this assumes that you are looking for 8 bytes total in a message
'if not change it to represent message length
Const protoMessLen As Integer = 8
Dim messBuf As List(Of Byte)
Do While buffer.Count >= protoMessLen
messBuf = buffer.GetRange(0, protoMessLen) 'get complete message
buffer.RemoveRange(0, protoMessLen) 'remove from buffer
' messBuf.ToArray 'converts list to array
'
'put code to process messBuf here
'
Loop
End Sub
-
Re: problem in handling multiple Bytes received using Serial Port
Hello All,
I changed the code of serial data received event handler to SerialPort1.ReadLine so that i do not have to convert back my data to 12 Bits. Below is the code. My data is coming and getting plotted on the picturebox but I feel that the plotting speed of data is slow. I mean I need maximum plotting speed of 100mm/sec . My per pixel size is 0.26mm so for 100 mm I need to cover 411 pixels approx in 1 Sec . How to achieve this speed? my picture box is 879 pixels wide.
Code:
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Try
While SerialPort1.BytesToRead > 0
inData1 = SerialPort1.ReadLine
inData2 = SerialPort1.ReadLine
inData3 = SerialPort1.ReadLine
inData4 = SerialPort1.ReadLine
Me.BeginInvoke((New MyDelegate(AddressOf Graph2Delegate)), inData1, inData2, inData3, inData4)
End While
Catch ex As Exception
End Try
End Sub
Private Sub Graph2Delegate()
analogData1Queue.Enqueue(indata1)
analogData2Queue.Enqueue(indata2)
analogData3Queue.Enqueue(indata3)
analogData4Queue.Enqueue(indata4)
dataenter = True
PrintX(inData1, inData2, inData3, inData4)
End Sub
Public Function PrintX(ByVal Xvalue1 As UInt32, ByVal Xvalue2 As UInt32, ByVal Xvalue3 As UInt32, ByVal Xvalue4 As UInt32) As Object
Try
x_start = 1
y_start1 = graph.Height - 100
y_start2 = graph.Height - 200
y_start3 = graph.Height - 300
y_start4 = graph.Height
mygraphics = graph.CreateGraphics
mygraphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
If dataenter = True Then
mygraphics.DrawLine(p2, x_start + k, y_start1 - temp_ahead1, x_start + (k + 1), y_start1 - Xvalue1)
temp_ahead1 = Xvalue1
mygraphics.DrawLine(p3, x_start + k, y_start2 - temp_ahead2, x_start + (k + 1), y_start2 - Xvalue2)
temp_ahead2 = Xvalue2
mygraphics.DrawLine(p4, x_start + k, y_start3 - temp_ahead3, x_start + (k + 1), y_start3 - Xvalue3)
temp_ahead3 = Xvalue3
mygraphics.DrawLine(p5, x_start + k, y_start4 - temp_ahead4, x_start + (k + 1), y_start4 - Xvalue4)
temp_ahead4 = Xvalue2
dataenter = False
k = k + 1
If k = graph.Width Then
k = 0
mygraphics.Clear(Color.White)
End If
End If
Catch ex As Exception
End Try
End Function
I'am sure there is a possibility to increase speed of plotting.
-
Re: problem in handling multiple Bytes received using Serial Port
Hello All,
I tried using the picture box paint event and kept calling it in my delegate using invalidate function, But it keeps refreshing . I mean I want the old data to remain on the picture box just like an oscilloscope !
Can anybody help me in using paint event to handle graph plotting!
-
Re: problem in handling multiple Bytes received using Serial Port
This feels like a moving target. In post #1 you said the device was sending 8 bytes which I provided code for. Now it looks like it is sending 4 lines.
If your handler receives one byte it is going to block until it receives 4 lines. Why is reading strings a better approach than reading numbers(bytes)?
-
Re: problem in handling multiple Bytes received using Serial Port
Hello dbasnett,
I just used readline method so that I dont have to break the data in 16 bits and sending it a packet of 2 BYTES. Both receiving and sending code will be reduced. But Anyways, I will go with which ever way is better . if I send data with out breaking at the controller END it looks like
printf("%d\n %d\n %d\n %d\n", DATA1,DATA2,DATA3,DATA4)
if I break it the MCU is sending the data as
UARTCharPut(Uart4,AH)
UARTCharPut(Uart4,AL)
UARTCharPut(Uart4,BH)
UARTCharPut(Uart4,BL)
UARTCharPut(Uart4,CH)
UARTCharPut(Uart4,CL)
UARTCharPut(Uart4,DH)
UARTCharPut(Uart4,DL)
Is there any difference in receiving Bytes rather than receiving Strings?
-
Re: problem in handling multiple Bytes received using Serial Port
Quote:
Originally Posted by
sumit11
...Is there any difference in receiving Bytes rather than receiving Strings?
Yes. If you are working with string methods the bytes are decoded based on the encoder chosen.
Both ends should agree, string data or byte data.
If it were me I would send and receive bytes if the data is numeric, which it seems to be in this case.
-
Re: problem in handling multiple Bytes received using Serial Port
Ok, Then I will go with sending and receiving Bytes . However, What I doubt is that will I be Able to achieve the plotting speed which I require Using PaintEvent of a control.
My ADC sampling Rate is 256 samples/Second . Basically I want the sweep speed till 100mm/Sec in my case. It can be greater ofcourse But will I be able to achieve it?
-
Re: problem in handling multiple Bytes received using Serial Port
I do that type of thing regularly.
I have data coming in from multiple sources at relatively fast frequency.
I update the GUI at 10hz, based on a timer tick.
The information coming in is stored in an area that the GUI can see it, so it updates the GUI with the latest data available for all items received, as necessary.
What I was thinking when you first posted, is that I would simply push whatever bytes were received into a concurrent queue, and that would take care of the RS-232 receiving side. It wouldn't do any pre-processing, it would just be responsible for receiving bytes as fast as they come in and push them on to a concurrent queue which would be available from both threads, with the control handshaking taken care of automatically.
Then, when the 10hz GUI timer tick occurs, the GUI code would pull the data from the queue in 8-byte chunks. If there were less than 8-bytes left in the queue, it would leave those there and pick them up the next tick.
You would then process all the chunks you've pulled from the queue, and update the display. If you want to update the GUI faster than 10hz, then you should be able to do that. The GUI updates should not hangup the serial data processing, and vice versa.
You would be updating the graph, not one data point at a time (256 hz), but several data points at a time. For instance, since the screen can't really refresh faster than 60 hz, if you used a timer firing at 15ms, it should end up firing at 64hz, so you would be updating 4 points in your plot per update.
As long as you're plotting all 256 points received per second, it probably would look fine even if you updated at 30hz or 20hz since the user will see all the points forming the wave shape in a reasonable approximation of real-time update.
-
Re: problem in handling multiple Bytes received using Serial Port
Quote:
Originally Posted by
sumit11
Ok, Then I will go with sending and receiving Bytes . However, What I doubt is that will I be Able to achieve the plotting speed which I require Using PaintEvent of a control.
My ADC sampling Rate is 256 samples/Second . Basically I want the sweep speed till 100mm/Sec in my case. It can be greater ofcourse But will I be able to achieve it?
If each sample is 8 bytes sent that equates to 16Kbps, so the serial port will not have a problem with that. Paint wise I don't know, but it seems reasonable.
-
Re: problem in handling multiple Bytes received using Serial Port
Hello Passel,
Whoa! That's a long explanation. I was able to understand the bottom line of your explanation that I will have to plot multiple points at a time.
I tried to implement the code given by dbasnett , However I'am not sure if I'am passing the data correctly using delegate.
How to break the data from the buffer into individual Bytes?
Code:
Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim BytesRead As Integer = 0
Dim numberOfBytesToRead As Integer = SerialPort1.BytesToRead
' Create a byte array large enough to hold the bytes to be read.
Dim newReceivedData(numberOfBytesToRead - 1) As Byte
' Read the bytes into the byte array.
BytesRead = SerialPort1.Read(newReceivedData, 0, numberOfBytesToRead)
If BytesRead > 0 Then
If BytesRead <> numberOfBytesToRead Then
Array.Resize(newReceivedData, BytesRead)
End If
buffer.AddRange(newReceivedData) 'accumulate in buffer
End If
'this assumes that you are looking for 8 bytes total in a message
'if not change it to represent message length
Const protoMessLen As Integer = 8
Dim messBuf As List(Of Byte)
Dim rcvBuf As Byte()
Do While buffer.Count >= protoMessLen
messBuf = buffer.GetRange(0, protoMessLen) 'get complete message
buffer.RemoveRange(0, protoMessLen) 'remove from buffer
rcvBuf = messBuf.ToArray() 'converts list to array
Me.Invoke(New Action(Of Byte())(AddressOf DrawGraph), rcvBuf)
Loop
End Sub
Private Sub DrawGraph(data As Byte())
Dim result1, result2, result3, result4, result5, result6, result7, result8 As UInt32
Dim indata1, indata2, indata3, indata4 As UInt32
result1 = data(0)
result3 = data(2)
result2 = data(1)
result4 = data(3)
result5 = data(4)
result6 = data(5)
result7 = data(6)
result8 = data(7)
indata1 = result1 Or (result2 << 8)
indata2 = result3 Or (result4 << 8)
indata3 = result5 Or (result6 << 8)
indata4 = result7 Or (result8 << 8)
End Sub
-
Re: problem in handling multiple Bytes received using Serial Port
Well, the code looks like it should work technically.
The code is simply saying
Code:
While there are 8 or more bytes in the received list of bytes.
Transfer 8 bytes from the received list to the msgBuf List {get first 8 bytes from received list and store in msgBuf list, remove the 8 bytes from the received list}
Copy the bytes (8 of them) from msgBuf to rcvBuf.
Invoke the DrawGraph sub on the Gui thread, passing the 8 byte array (rcvBuf) to DrawGraph.
Loop back to see if we have 8 or more bytes still in the received list
As for how to break the data from the buffer into individual Bytes, you've already done that, essentially. rcvBuf is a byte array so the first byte is in rcvBuf(0), the second byte in rcvBuf(1), etc...
But I don't know why you're asking about accessing the individual bytes at this point.
As for needing to draw multiple points at a time, it may not be absolutely necessary, but a lot depends on how you're doing the drawing. Is the Wave suppose to scroll, or will the wave draw from left to right, then start over at the left again. How efficient is the drawing, etc...
The main drawbacks I see with this approach is that DrawGraph will be called for each data point (i.e. 8 bytes), so you will need to redraw the graph for each set of 8 bytes received, unless you implement some kind of arbitrary point counter in DrawGraph that will collect the information from the messages and only actually draw the graph when it has received a number of points.
The other drawback is because you're using Invoke, the code will pause at that point, suspending in the DataReceived thread until the DrawGraph routine has finished doing whatever it is going to do. You could try using BeginInvoke instead. That will allow the code in the loop to continue and for your DataReceived event to finish without waiting for the DrawGraph call to complete. This means you will be processing the Data Received in parallel to the DrawGraph code running on the GUI thread. Because the messBuf bytes are copied into a local array and sent, perhaps the local array will not be clobbered by successive messages before being processed on the GUI thread, but I'm not sure.
Since what you have seems like it should work, have you tried it and not seen it work.
Since you added the invoke line, I'll assume it is expecting an array of bytes to be passed to it. If you put a breakpoint in the DrawGraph method, are you getting there and does the array passed have the 8 bytes you expect in it?
Are you trying to draw?
p.s. The code is also assuming that you are synchronized had haven't lost any bytes in the transfer so you're reading the 8 bytes that belong together, rather than some number of bytes from one message and the rest from the next.
For instance, the mouse protocol (back when mice were mostly serial) would usually be a 3 to 5 byte message, and the most significant bit of the first byte (which could be bit 7 for an 8n1 protocol, or bit 6 for a 7e1 or 7n1 protocol) would be set, but that bit would never be set in any of the other 2 to 4 bytes of data that made up the message. This allowed the reading software to identify what byte was the first in the sequence, and that when it saw that bit set again (the start of the next message) it could verify whether it collected the number of bytes it expected for a valid message. You might want to consider something like that if you have a known bit that won't be used for anything else in your data, or perhaps adding an extra byte with a set pattern that is sent first to sync up the receiver, so each transfer would be 9 bytes, with 8 bytes being your data, and the 1st byte just a synchronization character.
-
Re: problem in handling multiple Bytes received using Serial Port
Hello Passel,
I need to draw the graph from left to right, then start over at left again.
So, Suppose If I connect a sine wave analog signal on my four different ADC Input Channels(Pins), then My application need to plot the four individual sinewave's .
Since My ADC Data is 12 Bits so I need to break each pin output into two Bytes on sending end and then receive the two Bytes and "OR" them again to make 12 Bits(original Data) . So four channels means I've to send and receive total 8 Bytes. So every 8 Bytes means 4 original samples.
Also , I need to control the plotting speed(sweep) with different options like 5mm/Sec,10mm/Sec,25mm/Sec,50mm/Sec and 100mm/Sec.
So If I select 100mm/Sec, the graph plot should scroll upto 100mm in one Second.
The code which I've written with help of dbasnett (Thanks to him) till now is working. I checked by putting breakpoint in drawgraph function and also I used a richtextbox to display the incoming values. the incoming values match the ADC data.
indata1 value is equivalent to value of first pin of ADC
indata2 value is equivalent to value of second pin of ADC
indata3 value is equivalent to value of third pin of ADC
indata4 value is equivalent to value of fourth pin of ADC
Now My next step is to draw the graph. I've read that I should not use picturebox for my application since picturebox is designed to hold a single image. Then should I use a panel?
how to go about plotting the graph from here? I've not used paint handlers before .
-
Re: problem in handling multiple Bytes received using Serial Port
I can help with some of this
First, repalce
Code:
Me.Invoke(New Action(Of Byte())(AddressOf DrawGraph), rcvBuf)
with
Then your existing DrawGraph should look like this. There is a 'Stop' statement where your drawing code can go. The first part is to make sure that the code runs on the UI and that the buffer from serial port can't be overwritten
Code:
Private Sub DrawGraph(buf() As Byte)
Static bufLock As New Threading.AutoResetEvent(True)
bufLock.WaitOne()
Me.BeginInvoke(Sub()
'make a copy of buf
Dim bufCpy() As Byte = DirectCast(buf.Clone, Byte())
bufLock.Set() 'unlock the door for other calls
Dim indata1 As Integer = 0
Dim indata2 As Integer = 0
Dim indata3 As Integer = 0
Dim indata4 As Integer = 0
''create indata's from bufCpy
Const shftVal As Integer = 8
''long version
'indata1 = bufCpy(0)
'indata1 = indata1 << shftVal 'shift 8
'indata1 = indata1 Or bufCpy(1) 'or in second byte
'short version
indata1 = (CInt(bufCpy(0))) << shftVal Or bufCpy(1)
indata2 = (CInt(bufCpy(2))) << shftVal Or bufCpy(3)
indata3 = (CInt(bufCpy(4))) << shftVal Or bufCpy(5)
indata4 = (CInt(bufCpy(6))) << shftVal Or bufCpy(7)
Stop 'replace the Stop with your graph drawing code
End Sub)
End Sub
There is an assumption in this about how the twelve bits of data was sent from the external device, so the code may need to be tweaked to reflect the actual format.
-
Re: problem in handling multiple Bytes received using Serial Port
Hello dbasnett,
Thanks for the edits in the code. Yes, It is also working.
I tried plotting the graph with below code but the plotting speed is still a concern.
Code:
Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim BytesRead As Integer = 0
Dim numberOfBytesToRead As Integer = SerialPort1.BytesToRead
' Create a byte array large enough to hold the bytes to be read.
Dim newReceivedData(numberOfBytesToRead - 1) As Byte
' Read the bytes into the byte array.
BytesRead = SerialPort1.Read(newReceivedData, 0, numberOfBytesToRead)
If BytesRead > 0 Then
If BytesRead <> numberOfBytesToRead Then
Array.Resize(newReceivedData, BytesRead)
End If
buffer.AddRange(newReceivedData) 'accumulate in buffer
End If
'this assumes that you are looking for 8 bytes total in a message
'if not change it to represent message length
Const protoMessLen As Integer = 8
Dim messBuf As List(Of Byte)
Dim rcvBuf As Byte()
Do While buffer.Count >= protoMessLen
messBuf = buffer.GetRange(0, protoMessLen) 'get complete message
buffer.RemoveRange(0, protoMessLen) 'remove from buffer
rcvBuf = messBuf.ToArray() 'converts list to array
DrawGraph(rcvBuf)
Loop
End Sub
Private Sub DrawGraph(buf() As Byte)
Static bufLock As New Threading.AutoResetEvent(True)
bufLock.WaitOne()
Me.BeginInvoke(Sub()
'make a copy of buf
Dim bufCpy() As Byte = DirectCast(buf.Clone, Byte())
bufLock.Set() 'unlock the door for other calls
Dim indata1 As Integer = 0
Dim indata2 As Integer = 0
Dim indata3 As Integer = 0
Dim indata4 As Integer = 0
''create indata's from bufCpy
Const shftVal As Integer = 8
''long version
'indata1 = bufCpy(0)
'indata1 = indata1 << shftVal 'shift 8
'indata1 = indata1 Or bufCpy(1) 'or in second byte
'short version
indata1 = (CInt(bufCpy(0))) Or (bufCpy(1) << shftVal)
indata2 = (CInt(bufCpy(2))) Or (bufCpy(3) << shftVal)
indata3 = (CInt(bufCpy(4))) Or (bufCpy(5) << shftVal)
indata4 = (CInt(bufCpy(6))) Or (bufCpy(7) << shftVal)
PrintX(indata1)
End Sub)
End Sub
Public Function PrintX(ByVal Xvalue1 As UInt32) As Object
Try
x_start = 1
y_start1 = PictureBox1.Height - 10
mygraphics = PictureBox1.CreateGraphics
mygraphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
mygraphics.DrawLine(p2, x_start + k, y_start1 - temp_ahead1, x_start + (k + 1), y_start1 - Xvalue1)
temp_ahead1 = Xvalue1
k = k + 1
If k = PictureBox1.Width Then
k = 0
PictureBox1.Refresh()
End If
Catch ex As Exception
End Try
End Function
-
Re: problem in handling multiple Bytes received using Serial Port
what else could be the way to plot the graph?
-
Re: problem in handling multiple Bytes received using Serial Port
Quote:
Originally Posted by
sumit11
what else could be the way to plot the graph?
So seeing a little more I changed the approach slightly.
DrawGraph converts the data and adds it to a queue of a structure. It also starts a task to service the queue. The actual code for ALL drawing should go in GraphThem (I copied your code there).
With this the data reception and the actual drawing are no longer coupled. PrintX is gone. The actual graphics code will have to be done by someone else, not my strong point.
Code:
Private buffer As New List(Of Byte)
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim BytesRead As Integer = 0
Dim numberOfBytesToRead As Integer = SerialPort1.BytesToRead
' Create a byte array large enough to hold the bytes to be read.
Dim newReceivedData(numberOfBytesToRead - 1) As Byte
' Read the bytes into the byte array.
BytesRead = SerialPort1.Read(newReceivedData, 0, numberOfBytesToRead)
If BytesRead > 0 Then
If BytesRead <> numberOfBytesToRead Then
Array.Resize(newReceivedData, BytesRead)
End If
buffer.AddRange(newReceivedData) 'accumulate in buffer
End If
'this assumes that you are looking for 8 bytes total in a message
'if not change it to represent message length
Const protoMessLen As Integer = 8
Dim messBuf As List(Of Byte)
Dim rcvBuf As Byte()
Do While buffer.Count >= protoMessLen
messBuf = buffer.GetRange(0, protoMessLen) 'get complete message
buffer.RemoveRange(0, protoMessLen) 'remove from buffer
rcvBuf = messBuf.ToArray() 'converts list to array
DrawGraph(rcvBuf)
Loop
End Sub
Private Structure InData
Public indata1 As Integer
Public indata2 As Integer
Public indata3 As Integer
Public indata4 As Integer
End Structure
Private Sub DrawGraph(buf() As Byte)
'>>>>
'
Static DoPrint As Task
If DoPrint Is Nothing Then
DoPrint = Task.Run(Sub()
GraphThem()
End Sub)
End If
'
'<<<<
Static bufLock As New Threading.AutoResetEvent(True)
bufLock.WaitOne()
'make a copy of buf
Dim bufCpy() As Byte = DirectCast(buf.Clone, Byte())
bufLock.Set() 'unlock the door for other calls
Dim rcvData As New InData
''create indata's from bufCpy
Const shftVal As Integer = 8
'short version
rcvData.indata1 = (CInt(bufCpy(0))) Or (bufCpy(1) << shftVal)
rcvData.indata2 = (CInt(bufCpy(2))) Or (bufCpy(3) << shftVal)
rcvData.indata3 = (CInt(bufCpy(4))) Or (bufCpy(5) << shftVal)
rcvData.indata4 = (CInt(bufCpy(6))) Or (bufCpy(7) << shftVal)
InDataQ.Enqueue(rcvData)
InDataQHasNew.Set()
End Sub
Private InDataQ As New Concurrent.ConcurrentQueue(Of InData)
Private InDataQHasNew As New Threading.AutoResetEvent(False)
Public Sub GraphThem()
Do
InDataQHasNew.WaitOne() 'wait for signal
Do While InDataQ.Count > 0
Dim rcvData As InData
If InDataQ.TryDequeue(rcvData) Then
'got queue entry
Me.BeginInvoke(Sub()
Try
'inData1 - rcvData.indata1
x_start = 1
y_start1 = PictureBox1.Height - 10
mygraphics = PictureBox1.CreateGraphics
mygraphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
mygraphics.DrawLine(p2, x_start + k, y_start1 - temp_ahead1, x_start + (k + 1), y_start1 - rcvData.indata1)
temp_ahead1 = rcvData.indata1
k = k + 1
If k = PictureBox1.Width Then
k = 0
PictureBox1.Refresh()
End If
'indata2 - rcvData.indata2 etc.
Catch ex As Exception
End Try
End Sub)
Else
'get queue entry failed
Threading.Thread.Sleep(10) 'wait then try again
End If
Loop
Loop
End Sub
I have no way to test this but other than the drawing the code looks like it should work.
edit: You changed how the two bytes were combined to create one value. It seemed odd but I did not change it.
-
Re: problem in handling multiple Bytes received using Serial Port
Quote:
what else could be the way to plot the graph?
MS Chart tool may work, this guy has a couple of C# examples that look pretty good https://www.youtube.com/watch?v=HlDwVUR71yI
Plus there are neat features like scroll and zoom that can be added
-
Re: problem in handling multiple Bytes received using Serial Port
Hello dbasnett,
Well , I didnt try to understand your latest edited code , just copied and pasted for the moment, but still the same result.
wave plotting speed is same as it was using printx function!
I've seen MS Chart control and it plots the signal really fast. However not much tutorial stuff is available regarding MS Chart online!
specially the linegraph or spline graph feature.
-
Re: problem in handling multiple Bytes received using Serial Port
I've seen MS Chart control and it plots the signal really fast. However not much tutorial stuff is available regarding MS Chart on the net! specially the linegraph or spline graph feature.
I mean i'am trying to find how to display time in seconds in X-axis with MS Chart tool but no luck till now!!
This youtube link says kaychart. Is kaychart different from MS Chart??
-
Re: problem in handling multiple Bytes received using Serial Port
kayChart is a library for charting https://www.nuget.org/packages/kayChart.dll/
I have not used kayChart but I have used MS Chart, nothing at the speed that guys does but still ok for my purpose and uses the same techniques as kayChart.
As for the X axis unless you are transmitting the frequency value I don't see how that can accurately be gained from a serial connection, just changing the baud rate would alter your figures or a delay in buffering data. But I may be wrong on that and there may be a way of doing it that I am unaware of and a lot depends on the frequency you are trying to measure. People on this forum can probably give more input on that
-
Re: problem in handling multiple Bytes received using Serial Port
It's fairly easy to get started with an example, here is one that just requires a Form with a MSChart and a Timer.
Make sure the timer is enabled or nothing will happen, try zooming and scrolling
Code:
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
Dim chartdata As Double
Dim flipflop As Boolean = False
Private Sub updatechart()
Chart1.Series("Series1").Points.Add(chartdata)
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If flipflop Then
flipflop = False
chartdata = 22
ElseIf flipflop = False Then
flipflop = True
chartdata = 45
End If
updatechart()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Chart1.Series("Series1").ChartType = SeriesChartType.Spline
With Chart1.ChartAreas(0).AxisX
.Minimum = 0
.Maximum = 2000
.MajorGrid.Interval = 5
.MajorTickMark.Enabled = True
.MajorTickMark.Interval = 5
.MajorTickMark.Size = 5
.LabelStyle.Enabled = True
.LabelStyle.Interval = 5
End With
Chart1.ChartAreas(0).CursorX.IsUserEnabled = True
Chart1.ChartAreas(0).CursorX.IsUserSelectionEnabled = True
Chart1.ChartAreas(0).AxisX.ScaleView.Zoomable = True
Chart1.ChartAreas(0).AxisX.ScrollBar.IsPositionedInside = True
Chart1.ChartAreas(0).AxisX.ScaleView.Zoom(0, 200)
End Sub
End Class
-
Re: problem in handling multiple Bytes received using Serial Port
hello Mc_Vb
I followed your example code and Tried running it. I came up with my code to use MS chart. below is the same.
I'am just wondering when should I update my graph with new data. Should I do it with Timer tick OR in the delegate function where Iam receiving new data from serial port. I tried plotting the graph with below code but it is too fast. Can't really figure out anything. plotting graph with random number using rand function inTimer tick is a different thing.
But plotting with Real time Serial Port data is the thing I just cant figure out.
Code:
Imports System.IO.Ports
Imports System.Threading
Imports System.Text
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
Dim result1, result2, result3, result4, result5, result6, result7, result8 As UInt32
Dim indata1, indata2, indata3, indata4, indata5, indata6, indata7, indata8 As UInt32
Dim rcvddata(4000) As UInt32
Dim j As Integer
Public counter As Integer = 0
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
SerialPort1.PortName = ComboBox1.SelectedItem
If SerialPort1.IsOpen() = False Then
SerialPort1.Open()
End If
SerialPort1.Write("a")
Catch ex As Exception
End Try
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
SerialPort1.Write("b")
If SerialPort1.IsOpen() Then
SerialPort1.Close()
End If
End Sub
' Dim indata1, indata2, indata3, indata4 As Byte
Public Delegate Sub MyDelegate(ByVal data As Byte)
Private buffer As New List(Of Byte)
Dim messBuf As List(Of Byte)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each Port As String In SerialPort.GetPortNames()
ComboBox1.Items.Add(Port)
Next Port
If ComboBox1.Items.Count > 0 Then
ComboBox1.SelectedIndex = 0
End If
SerialPort1.RtsEnable = True
SerialPort1.BaudRate = "115200"
SerialPort1.Parity = Parity.None
SerialPort1.DataBits = 8
SerialPort1.StopBits = StopBits.One
SerialPort1.Encoding = Encoding.ASCII
Chart1.Titles.Add("Sin Wave Example")
Chart1.Titles(0).Font = New Font("Arial", 10, FontStyle.Bold)
With Chart1.ChartAreas(0)
.AxisX.Title = "Counter"
.AxisX.MajorGrid.Enabled = False
.AxisX.MajorGrid.LineColor = Color.LightGray
.AxisX.Interval = 50
.AxisX.IsLabelAutoFit = False
.AxisX.LabelStyle.Font = New Font("Arial", 10, FontStyle.Regular)
.AxisX.LabelStyle.Angle = -90
.AxisX.LabelStyle.IsStaggered = False
.AxisX.LabelStyle.Enabled = True
.AxisY.Title = "Values"
.AxisY.MajorGrid.LineColor = Color.LightGray
.AxisY.IsInterlaced = True
.AxisY.InterlacedColor = Color.FloralWhite
.BackColor = Color.FloralWhite
.BackSecondaryColor = Color.White
.BackGradientStyle = GradientStyle.HorizontalCenter
.BorderColor = Color.Blue
.BorderDashStyle = ChartDashStyle.Solid
.BorderWidth = 1
.ShadowOffset = 2
End With
End Sub
Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim BytesRead As Integer = 0
Dim numberOfBytesToRead As Integer = SerialPort1.BytesToRead
' Create a byte array large enough to hold the bytes to be read.
Dim newReceivedData(numberOfBytesToRead - 1) As Byte
' Read the bytes into the byte array.
BytesRead = SerialPort1.Read(newReceivedData, 0, numberOfBytesToRead)
If BytesRead > 0 Then
If BytesRead <> numberOfBytesToRead Then
Array.Resize(newReceivedData, BytesRead)
End If
buffer.AddRange(newReceivedData) 'accumulate in buffer
End If
'this assumes that you are looking for 8 bytes total in a message
'if not change it to represent message length
Const protoMessLen As Integer = 8
Dim messBuf As List(Of Byte)
Dim rcvBuf As Byte()
Do While buffer.Count >= protoMessLen
messBuf = buffer.GetRange(0, protoMessLen) 'get complete message
buffer.RemoveRange(0, protoMessLen) 'remove from buffer
rcvBuf = messBuf.ToArray() 'converts list to array
Me.BeginInvoke(New Action(Of Byte())(AddressOf DrawGraph), rcvBuf)
Loop
End Sub
Structure DataPointType
Public x As UInt32
Public y As UInt32
End Structure
Private Sub DrawGraph(Data() As Byte)
result1 = Data(0)
result2 = Data(1)
'result3 = Data(2)
'result4 = Data(3)
'result5 = Data(4)
'result6 = Data(5)
'result7 = Data(6)
'result8 = Data(7)
indata1 = result1 Or (result2 << 8)
'indata2 = result3 Or (result4 << 8)
'indata3 = result5 Or (result6 << 8)
'indata4 = result7 Or (result8 << 8)
GetDataPoint()
Chart1.Series.Clear()
Chart1.Series.Add("Line Type")
Chart1.Series(0).IsVisibleInLegend = False
Chart1.Series(0).Color = Color.Red
Chart1.Series(0).ChartType = DataVisualization.Charting.SeriesChartType.Spline
Chart1.ChartAreas(0).AxisX.Minimum = ChartDataList(0).x
Chart1.ChartAreas(0).AxisX.Maximum = Chart1.ChartAreas(0).AxisX.Minimum + 360
For i = 0 To ChartDataList.Count - 1
Chart1.Series(0).Points.AddXY(ChartDataList(i).x, ChartDataList(i).y)
Next
End Sub
Private ChartDataList As New List(Of DataPointType)
Private Sub GetDataPoint()
'remove a point
If ChartDataList.Count > 512 Then
ChartDataList.Remove(ChartDataList(0))
End If
'add next point
Dim thisDataPoint As DataPointType
thisDataPoint.x = counter
thisDataPoint.y = indata1
ChartDataList.Add(thisDataPoint)
counter += 20
End Sub
End Class
-
Re: problem in handling multiple Bytes received using Serial Port
this is the reference and what I need to do
https://www.youtube.com/watch?v=nvBzT660gY4
-
Re: problem in handling multiple Bytes received using Serial Port
Hi, the timer was just to display an example and in your case I would use fixed values for the X axis. The data received event would take the place of the timer and update the chart.
You said in an earlier post that you were sampling at 256 samples per second so I would make my major grid interval 256.
I would make my minor grid interval 1 and leave it disabled initially, each sample would leave a data point on the minor tick marks and there would be 256 minor tick/grid marks per second ( just under 4 mSec a sample )
The reason I would leave the minor grid/tick disabled initially would be clarity, I would perhaps use a button or checkbox to "switch" the minor grid on and off whenever I zoomed into the chart, here is a sample
Code:
Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
If CheckBox1.Checked Then
With Chart1.ChartAreas(0).AxisX
.MinorGrid.Enabled = True
.MinorTickMark.Enabled = True
.MinorGrid.Interval = 1
.MinorTickMark.Interval = 1
.MinorTickMark.Size = 5
End With
Else
With Chart1.ChartAreas(0).AxisX
.MinorGrid.Enabled = False
.MinorTickMark.Enabled = False
End With
End If
End Sub
I would also adjust the initial scale of the chart.
Code:
Chart1.ChartAreas(0).AxisX.ScaleView.Zoom(0, 600)
Looking at the progress you have made with MSChart you probably have more knowledge of it than I do, what I say is not set in stone they are just the thoughts I might have if I were to tackle the same problem.
-
Re: problem in handling multiple Bytes received using Serial Port
Hello All,
Finally I've been able to plot the graph with different sweep speeds . Thanks to dbasnett for helping me out!!
I didn't use MS Chart for now because learning would take me long time and I have my project deadline in July!
Cheers !!!