0

I am collecting data from an Arduino and transmit on Visual Basic through the serial port. Now I wanted to plot a graph with time vs electrical energy (unit kWh) - time along the x-axis and electrical energy along the y-axis. Usually I am getting data of the current from Arduino.

Now I wanted to learn how to start plotting the graph. I need a simple example explaining plotting a graph for sample. I tried some example code. It seems they are not working.

How do I plot a graph time vs current read from serial? Once Visual Basic starts running, it should save data w.r.t system time and date.

Example available here

Current code

Imports System
Imports System.IO.Ports
Imports System.ComponentModel
Imports System.Threading

Imports System.Drawing

Public Class Form1
    Dim myPort As Array
    Dim Distance As Integer


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        myPort = IO.Ports.SerialPort.GetPortNames()
        PortComboBox.Items.AddRange(myPort)
        BaudComboBox.Items.Add(9600)
        BaudComboBox.Items.Add(19200)
        BaudComboBox.Items.Add(38400)
        BaudComboBox.Items.Add(57600)
        BaudComboBox.Items.Add(115200)
        ConnectButton.Enabled = True
        DisconnectButton.Enabled = False
    End Sub

    Private Sub ConnectButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ConnectButton.Click
        SerialPort1.PortName = PortComboBox.Text
        SerialPort1.BaudRate = BaudComboBox.Text
        SerialPort1.Open()
        Timer1.Start()

        'lblMessage.Text = PortComboBox.Text & " Connected."
        ConnectButton.Enabled = False
        DisconnectButton.Enabled = True
    End Sub

    Private Sub DisconnectButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DisconnectButton.Click
        SerialPort1.Close()

        DisconnectButton.Enabled = False
        ConnectButton.Enabled = True
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Try
            SerialPort1.Write("c")
            System.Threading.Thread.Sleep(250)
            Dim k As Double
            Dim distance As String = SerialPort1.ReadLine()
            k = CDbl(distance)
            ListBoxSensor.Text = k
        Catch ex As Exception

        End Try
    End Sub

    Private Sub Relay_ON_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Relay_ON.Click
        SerialPort1.Write("1")
    End Sub

    Private Sub Relay_Off_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Relay_Off.Click
        SerialPort1.Write("0")
    End Sub

End Class
Community
  • 1
  • 1
user50949
  • 37
  • 2
  • 7

1 Answers1

0

Add a picturebox, a timer a button and a textbox:

PictureBox1 size = 768, 279
timer interval to 500

Private img As Bitmap
Private imgClone As Bitmap
Private widthInterval As Integer
'the distance from the left side of picturebox where x axis starts
Private leftPad As Integer = 50
'the distance from the down side of picturebox where x axis is
Private downPad As Integer = 30
'the distance from the up side of picturebox where y axis ends
Private upPad As Integer = 50
'the distance from the right side of picturebox where x axis ends
Private rightPad As Integer = 80
Private rn As New Random

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    drawBack()

    Timer1.Enabled = True
End Sub

Private Sub drawBack()
    Static count As Boolean = False
    Dim g As Graphics
    'number of values in x axis e.g 1, 2, 3, ... representing time
    Dim numX As Integer = 23
    'number of values in y axis representing KW/h
    Dim numY As Integer = 5
    Dim stringFormat As New StringFormat()
    'arreys to hold the text for both axies
    Dim arrayTextX(numX), arrayTextY(numY - 1) As String
    Dim i As Integer
    'the distance from the right side of picturebox where x axis stops
    Dim rightPad As Integer = 80
    Dim brush As Brush = New SolidBrush(Color.FromArgb(245, 255, 255))
    Dim pen As Pen = New Pen(Color.FromArgb(212, 212, 212), 1)
    Dim height, x, y As Integer

    'Run once
    If count = True Then
        Return
    End If

    count = True

    stringFormat.Alignment = StringAlignment.Center

    img = New Bitmap(PictureBox1.Width, PictureBox1.Height)
    imgClone = New Bitmap(PictureBox1.Width, PictureBox1.Height)

    g = Graphics.FromImage(img)
    g.SmoothingMode = SmoothingMode.AntiAlias
    g.Clear(Color.White)

    'the distance in x axis between each value
    widthInterval = CInt((PictureBox1.Width - leftPad - rightPad) / (numX + 1))
    'the distance in y axis between each value
    height = CInt((PictureBox1.Height - upPad - downPad) / (numY + 1))

    'fill arrays with text
    For i = 0 To numX - 1
        arrayTextX(i) = (i + 1).ToString
    Next

    For i = 0 To numY - 1
        arrayTextY(i) = ((i + 1) * height).ToString
    Next

    'fill background of graph with color
    g.FillRectangle(brush, New Rectangle(leftPad, upPad, PictureBox1.Width - leftPad - rightPad + 1, _
                                         PictureBox1.Height - downPad - upPad))
    'vertical lines
    x = leftPad
    For i = 0 To numX - 1
        x += widthInterval 
        g.DrawLine(pen, x, PictureBox1.Height - downPad, x, upPad)
        g.DrawString(arrayTextX(i), New Font("Arial", 8), Brushes.Black, _
                     New Rectangle(x - 10, PictureBox1.Height - downPad + 3, 20, 20), stringFormat)
    Next

    'horizontal lines
    stringFormat.Alignment = StringAlignment.Far
    y = PictureBox1.Height - downPad
    For i = 0 To numY - 1
        y -= height
        g.DrawLine(pen, leftPad, y, PictureBox1.Width - rightPad, y)
        g.DrawString(arrayTextY(i), New Font("Arial", 8), Brushes.Black, _
                     New Rectangle(0, y - 6, leftPad - 5, 20), stringFormat)
    Next

    g.DrawString("KW/Hour", New Font("Arial", 8, FontStyle.Bold), Brushes.Black, _
                     New PointF(5, 5))
    g.DrawString("Time", New Font("Arial", 8, FontStyle.Bold), Brushes.Black, _
                     New PointF(PictureBox1.Width - 50, PictureBox1.Height - 20))

    'draws x axis
    g.DrawLine(Pens.Black, New Point(leftPad, PictureBox1.Height - downPad), _
               New Point(PictureBox1.Width - rightPad, PictureBox1.Height - downPad))
    'draws y axis
    g.DrawLine(Pens.Black, New Point(leftPad, PictureBox1.Height - downPad), _
               New Point(leftPad, upPad))

    g.Dispose()

    PictureBox1.Image = img
    imgClone = CType(img.Clone, Bitmap)
End Sub

Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
    Dim value, x As Integer
    Dim g As Graphics
    Dim pntEnd As Point
    Static pnt As Point = New Point(-1, -1)
    Static staticX As Integer = -1

    Dim hour As Integer = DateTime.Now.Hour
    Dim minute As Integer = DateTime.Now.Minute
    Dim second As Integer = DateTime.Now.Second

    second = minute * 60 + second

    x = leftPad + hour * widthInterval + CInt(CDbl(widthInterval - 1) * CDbl(second) / 3600.0R)

    If pnt.X >= 0 Then
        'checks if the new points x coordinate is the same as the previous and returns
        If x <= staticX Then
            Return
        End If
    End If

    GetValue(value)

    pntEnd = New Point(x, PictureBox1.Height - value - downPad)

    g = Graphics.FromImage(img)
    g.SmoothingMode = SmoothingMode.AntiAlias

    If pnt.X < 0 Then
        g.DrawLine(Pens.Red, pntEnd, pntEnd)
    Else
        g.DrawLine(Pens.Red, pnt, pntEnd)
    End If

    g.Dispose()

    pnt = pntEnd
    staticX = x

    PictureBox1.Invalidate()
End Sub

Private Sub GetValue(ByRef value As Integer)
    'here you can take the value from arduino.
    value = rn.Next(0, PictureBox1.Height - downPad - upPad) 'random value
End Sub

When you press Button1 the graph starts.

Calculate width of graph (x axis)

width = PictureBox1.Width - leftPad - rightPad

this width is equivelant with 24h or 86400sec. So you should set the timer interval to

Timer1.Interval = CInt((86400 / width ) * 1000) 'in milliseconds

There is no need to be that because the tick function checks if the new point is the same as the previous. So set timer interval to 1 second.