VS 2015 graph plotting speed is slow using Serial Port data-VBForums
Results 1 to 6 of 6

Thread: graph plotting speed is slow using Serial Port data

  1. #1

    Thread Starter
    New Member
    Join Date
    Jul 2017
    Posts
    14

    graph plotting speed is slow using Serial Port data

    Hi All,

    I'am using VS2015 and I'am trying to plot real time graph inside a picture box using Serial Port data. My issue is that the speed of graph is slow and it further keeps slowing down on it own after few seconds.
    I think @115200 baudrate the graph plotting speed should be really fast!! Below is the screen shot of my GUI

    Name:  slow graph.jpg
Views: 115
Size:  34.8 KB

    and below is the code for data recieved event and plotting function

    Code:
    Private Sub myPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles myPort.DataReceived
    
           
    
          
           
            Try
                inData1 = myPort.ReadByte()
                inData2 = myPort.ReadByte()
                inData3 = myPort.ReadByte()
                inData4 = myPort.ReadByte()
               
    
                DataEnter = True
                analogDataQueue.Enqueue(inData1)
                 Serial_data_tmp = inData1
                Serial_array(Serial_data_count) = Chr(CInt(Serial_data_tmp))        'ASCII        
                Serial_data_count = Serial_data_count + 1
    
               
               
                PrintX(inData1, inData2, inData3, inData4)
                
    
                If Serial_data_count = 89961 Then
                    Serial_data_count = 0
                End If
    
            Catch ex As Exception
            End Try
    
         
    
        End Sub
    
    Function PrintX(ByVal Xvalue1 As Byte, ByVal Xvalue2 As Byte, ByVal Xvalue3 As Byte, ByVal Xvalue4 As Byte)
            'Private Sub printx()
            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.PageUnit = GraphicsUnit.Pixel
                mygraphics.PageScale = 1
                mygraphics.DrawLine(p1, 0, CInt(graph.Height), CInt(graph.Width), CInt(graph.Height))
                mygraphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
                mygraphics.ScaleTransform(1, 1)
    
                If DataEnter = True Then
    
                    data1 = Xvalue1
    
                    data2 = Xvalue2
    
                    data3 = Xvalue3
    
                    data4 = Xvalue4
    
                    ' Xvalue2 = Xvalue2 / 2
                    If CheckBox1.Checked Then
    
                        ' data1 = data1 / 2
                        mygraphics.DrawLine(p2, x_start + k, y_start1 - temp_ahead1, x_start + (k + 1), y_start1 - data1)
                        temp_ahead1 = data1
                    End If
    
                    If CheckBox2.Checked Then
    
                        mygraphics.DrawLine(p2, x_start + k, y_start1 - temp_ahead1, x_start + (k + 1), y_start1 - data1)
                        temp_ahead1 = data1
    
                        mygraphics.DrawLine(p3, x_start + k, y_start2 - temp_ahead2, x_start + (k + 1), y_start2 - data2)
                        temp_ahead2 = data2
                    End If
                    If CheckBox3.Checked Then
    
                        mygraphics.DrawLine(p2, x_start + k, y_start1 - temp_ahead1, x_start + (k + 1), y_start1 - data1)
                        temp_ahead1 = data1
    
                        mygraphics.DrawLine(p3, x_start + k, y_start2 - temp_ahead2, x_start + (k + 1), y_start2 - data2)
                        temp_ahead2 = data2
                        mygraphics.DrawLine(p4, x_start + k, y_start3 - temp_ahead3, x_start + (k + 1), y_start3 - data3)
                        temp_ahead3 = data3
                    End If
    
                    If CheckBox4.Checked Then
                        mygraphics.DrawLine(p2, x_start + k, y_start1 - temp_ahead1, x_start + (k + 1), y_start1 - data1)
                        temp_ahead1 = data1
    
                        mygraphics.DrawLine(p3, x_start + k, y_start2 - temp_ahead2, x_start + (k + 1), y_start2 - data2)
                        temp_ahead2 = data2
                        mygraphics.DrawLine(p4, x_start + k, y_start3 - temp_ahead3, x_start + (k + 1), y_start3 - data3)
                        temp_ahead3 = data3
    
                        mygraphics.DrawLine(p5, x_start + k, y_start4 - temp_ahead4, x_start + (k + 1), y_start4 - data4)
                        temp_ahead4 = data4
                    End If
                End If
    
    
    
    
    
                DataEnter = False
    
    
                    k = k + 1
    
                    If k = graph.Width Then
                        k = 0
                        mygraphics.Clear(Color.White)
    
                    End If
    
    
                    'End While
    
    
    
            Catch ex As Exception
            End Try
        End Function

  2. #2
    Still learning kebo's Avatar
    Join Date
    Apr 2004
    Location
    Gardnerville,nv
    Posts
    3,616

    Re: graph plotting speed is slow using Serial Port data

    From my experience, there is no way GDI can keep up with a 115200 serial port baud rate. GDI is simply not that fast. As you add more and more points, GDI will further slow down as you are seeing.

    Rather than update the graph in real time when a data point comes in, you should store all of your points in a class level array or list of some sort and update the graph using a timer. At least then you have a little more control over the situation.

    I'm not sure what type of control you are using for your graph, but if it is a picture box or panel, you should be handling the drawing the control's paint event handler and using the graphics object provided.

    If you really need to draw a boat load of data point AND you need a fast update rate, you will need to ditch using GDI altogether and move to a DirectX approach.
    Process control doesn't give you good quality, it gives you consistent quality.
    Good quality comes from consistently doing the right things.

    Vague general questions have vague general answers.
    A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.

    ______________________________
    Last edited by kebo : Now. Reason: superfluous typo's

  3. #3
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Pointless Forest 38.517,-92.023
    Posts
    8,940

    Re: graph plotting speed is slow using Serial Port data

    This code may help get the data cleaner / faster. Note that the call to PrintX is moved to method Protocol.

    Code:
    Imports System.IO.Ports
    
    Public Class Form1
    
        Private isRun As New Threading.ManualResetEvent(False)
    
        Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
    
            'this thread reads bytes and adds them to the buffer
            rcvdThrd = New Threading.Thread(AddressOf Receive)
            rcvdThrd.IsBackground = True
    
            'this thread examines the buffer to see if a 
            'complete message, as defined by the protocol, is available
            procprotoThrd = New Threading.Thread(AddressOf Protocol)
            procprotoThrd.IsBackground = True
    
            isRun.Set() 'we are running
    
            'start threads that do the work
            procprotoThrd.Start()
            rcvdThrd.Start()
            Try
                'Modify settings as needed <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                sp.PortName = "COM1"
                sp.BaudRate = 115200
                sp.DataBits = 8
                sp.Parity = Parity.None
                sp.StopBits = StopBits.One
                sp.WriteTimeout = 100
                sp.Encoding = System.Text.Encoding.GetEncoding(28591) 'default is 7 bit ascii, this is 8 bit
                sp.Open()
    
            Catch ex As Exception
                Stop
            End Try
    
        End Sub
    
        Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
            isRun.Reset()
            Threading.Thread.Sleep(100)
            rcvd.Set()
            Threading.Thread.Sleep(100)
            procproto.Set()
            Threading.Thread.Sleep(100)
            If sp.IsOpen Then sp.Close()
            Threading.Thread.Sleep(100)
        End Sub
    
        Private WithEvents sp As New SerialPort
    
        Private Sub sp_DataReceived(sender As Object, _
                                    e As SerialDataReceivedEventArgs) _
                                Handles sp.DataReceived
            rcvd.Set() 'read the bytes, see method Receive
        End Sub
    
        Private Sub sp_ErrorReceived(sender As Object, _
                                     e As SerialErrorReceivedEventArgs) Handles sp.ErrorReceived
            Debug.WriteLine(e.EventType.ToString)
        End Sub
    
        Private Sub sp_PinChanged(sender As Object, _
                                  e As SerialPinChangedEventArgs) Handles sp.PinChanged
            Debug.WriteLine(e.EventType.ToString)
        End Sub
    
        Private rcvd As New Threading.AutoResetEvent(False)
        Private rcvdThrd As Threading.Thread
    
        Private Sub Receive()
            Do
                If sp.IsOpen Then 'is port open
                    Dim numb As Integer = sp.BytesToRead 'number of bytes to read
                    If numb > 0 Then
                        Dim temp(numb - 1) As Byte 'create a temporary buffer
                        Try
                            numb = sp.Read(temp, 0, numb) 'read the bytes
                            If numb <> temp.Length Then
                                Array.Resize(temp, numb)
                            End If
                            'add temp buffer to public buffer
                            Threading.Monitor.Enter(rcvBufLock)
                            rcvBuf.AddRange(temp)
                            Threading.Monitor.Exit(rcvBufLock)
                            procproto.Set() 'check for possible message, see method Protocol
                        Catch ex As Exception
                            'fix error handler
                        End Try
                    End If
                    If sp.BytesToRead = 0 Then
                        rcvd.WaitOne() 'wait for event handler to fire
                    End If
                End If
            Loop While isRun.WaitOne(0)
        End Sub
    
        Private rcvBuf As New List(Of Byte)
        Private rcvBufLock As New Object
    
        Private procproto As New Threading.AutoResetEvent(False)
        Private procprotoThrd As Threading.Thread
    
        Private Sub Protocol()
            Dim messRcvdCt As Integer = 0
            Dim newData As New List(Of DeviceProto)
            Do
                Dim initBufSZ As Integer = rcvBuf.Count
                Dim aMess As DeviceProto
                Do
                    aMess = New DeviceProto(rcvBuf, rcvBufLock) 'potential message?
                    'yes
                    If aMess.isValid Then
                        newData.Add(aMess)
                        messRcvdCt += 1
                    End If
                Loop While Not aMess.Incomplete
    
                If newData.Count > 0 Then 'new messages
                    'yes
                    For Each mp As DeviceProto In newData
                        PrintX(mp.inData1, mp.inData2, mp.inData3, mp.inData4) '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                    Next
                    newData.Clear()
                End If
                procproto.WaitOne() 'wait for signal to proccess next potential message
            Loop While isRun.WaitOne(0)
        End Sub
    
        Private Class DeviceProto
            Private valid As Boolean = False
            Private _incomplete As Boolean = True
            Private theData() As Byte
            Public Sub New(ByRef someData As List(Of Byte), ByRef someDataLock As Object)
                'protocol is:
                '4 bytes of data
                '
                If someData.Count >= 4 Then 'min message size is 4 bytes
                    If Threading.Monitor.TryEnter(someDataLock) Then
                        Me.theData = someData.GetRange(0, 4).ToArray
                        someData.RemoveRange(0, 4)
                        Me.valid = True
                        Me._incomplete = False
                        Threading.Monitor.Exit(someDataLock)
                    Else
                        Debug.WriteLine(someData.Count)
                    End If
                End If
            End Sub
    
            Public Function Incomplete() As Boolean
                Return Me._incomplete
            End Function
    
            Public Function isValid() As Boolean
                Return Me.valid
            End Function
    
            Public Function inData1() As Byte
                Dim rv As Byte = 255
                If Me.valid Then rv = Me.theData(0)
                Return rv
            End Function
    
            Public Function inData2() As Byte
                Dim rv As Byte = 255
                If Me.valid Then rv = Me.theData(1)
                Return rv
            End Function
    
            Public Function inData3() As Byte
                Dim rv As Byte = 255
                If Me.valid Then rv = Me.theData(2)
                Return rv
            End Function
    
            Public Function inData4() As Byte
                Dim rv As Byte = 255
                If Me.valid Then rv = Me.theData(3)
                Return rv
            End Function
        End Class
    
    End Class
    edit: if you zip the project and make it available I'll take a look. If each message is 4 bytes it means that at 115,200 you are receiving 3,600 messages / second max or a message every 0.000277 seconds. Hard to keep up with that.
    Last edited by dbasnett; Oct 4th, 2017 at 11:49 AM.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein
    "They who can give up essential liberty to obtain a little temporary safety, deserve neither liberty nor safety." Benjamin Franklin
    "It is not all that I know that is the problem, it is all I think I do that is."

  4. #4
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    31,096

    Re: graph plotting speed is slow using Serial Port data

    One thing about DB's code: He's doing the receiving in a background thread. I thought the serial port DataReceived thread was ALWAYS raised on a different thread, but I admit it has been years since I've worked with it. Maybe it was just that I always used it on a background thread. Anyways, it should be on a different thread, or else your reading is going to get in the way of the drawing.

    There's a fair amount of wasted time in the code you showed, but as Kebo said, you're probably at or exceeding the limit of what GDI can do. There are some ways you can speed up the code you have. For example, you are calling CreateGraphics, which I haven't timed, but it seems like it could be an issue. The Paint event handler gives you a graphics object in the e argument.

    There are a few other minor things, like some inefficient conversions, but what is potentially the biggest is the abuse of Try...Catch. You are ignoring any exceptions raised. That's bad news, because exception handling is free if no exceptions are thrown, but is VERY time consuming if one is thrown. It may be as simple as you getting an exception every time through, which would slow your code to a crawl, even if the rest were perfect. If there are exceptions, you need to know about them, because you need to find a way to avoid them without using the exception handlers. Having said that, you 'might' want to ignore some exceptions in DataReceived, but ONLY if that event is being raised on a different thread. If it's on the UI thread, you can't afford to ignore exceptions.
    My usual boring signature: Nothing

  5. #5
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Pointless Forest 38.517,-92.023
    Posts
    8,940

    Re: graph plotting speed is slow using Serial Port data

    Quote Originally Posted by Shaggy Hiker View Post
    One thing about DB's code: He's doing the receiving in a background thread. I thought the serial port DataReceived thread was ALWAYS raised on a different thread, but I admit it has been years since I've worked with it. Maybe it was just that I always used it on a background thread. Anyways, it should be on a different thread, or else your reading is going to get in the way of the drawing...
    You are correct. I am doing as little as possible in the DataReceived event handler since only one SerialPort event can be raised at a time.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein
    "They who can give up essential liberty to obtain a little temporary safety, deserve neither liberty nor safety." Benjamin Franklin
    "It is not all that I know that is the problem, it is all I think I do that is."

  6. #6
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,143

    Re: graph plotting speed is slow using Serial Port data

    Quote Originally Posted by dbasnett View Post
    You are correct. I am doing as little as possible in the DataReceived event handler since only one SerialPort event can be raised at a time.
    This is a good little protip I never really thought about in terms of dealing with a high data rate from the SerialPort class.
    Nothing I post is production-ready. It is provided as-is, use it at your own risk.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width

Survey posted by VBForums.